Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Populate capabilities in WifiEnumerateAccessPoints API #117

Merged
merged 8 commits into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
293 changes: 284 additions & 9 deletions src/common/service/NetRemoteService.cxx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@

#include <algorithm>
#include <format>
#include <iterator>
#include <string>
#include <vector>

#include <microsoft/net/remote/NetRemoteService.hxx>
#include <microsoft/net/wifi/IAccessPoint.hxx>
#include <microsoft/net/wifi/IAccessPointController.hxx>
#include <plog/Log.h>

using namespace Microsoft::Net::Remote::Service;

using Microsoft::Net::Wifi::AccessPointControllerException;
using Microsoft::Net::Wifi::AccessPointManager;
using Microsoft::Net::Wifi::IAccessPoint;

NetRemoteService::NetRemoteService(std::shared_ptr<AccessPointManager> accessPointManager) :
m_accessPointManager(std::move(accessPointManager))
Expand All @@ -21,26 +26,296 @@ NetRemoteService::GetAccessPointManager() noexcept
return m_accessPointManager;
}

namespace detail
{
Microsoft::Net::Wifi::Dot11PhyType
IeeeProtocolToNetRemotePhyType(Microsoft::Net::Wifi::IeeeProtocol ieeeProtocol)
{
using Microsoft::Net::Wifi::Dot11PhyType;
using Microsoft::Net::Wifi::IeeeProtocol;

Dot11PhyType phyType{ Dot11PhyType::Dot11PhyTypeUnknown };

switch (ieeeProtocol) {
case IeeeProtocol::B:
phyType = Dot11PhyType::Dot11PhyTypeB;
break;
case IeeeProtocol::G:
phyType = Dot11PhyType::Dot11PhyTypeG;
break;
case IeeeProtocol::N:
phyType = Dot11PhyType::Dot11PhyTypeN;
break;
case IeeeProtocol::A:
phyType = Dot11PhyType::Dot11PhyTypeA;
break;
case IeeeProtocol::AC:
phyType = Dot11PhyType::Dot11PhyTypeAC;
break;
case IeeeProtocol::AD:
phyType = Dot11PhyType::Dot11PhyTypeAD;
break;
case IeeeProtocol::AX:
phyType = Dot11PhyType::Dot11PhyTypeAX;
break;
case IeeeProtocol::BE:
phyType = Dot11PhyType::Dot11PhyTypeBE;
break;
default:
break;
}

return phyType;
}

Microsoft::Net::Wifi::RadioBand
IeeeFrequencyBandToNetRemoteRadioBand(Microsoft::Net::Wifi::IeeeFrequencyBand ieeeFrequencyBand)
{
using Microsoft::Net::Wifi::IeeeFrequencyBand;
using Microsoft::Net::Wifi::RadioBand;

RadioBand band{ RadioBand::RadioBandUnknown };

switch (ieeeFrequencyBand) {
case IeeeFrequencyBand::TwoPointFourGHz:
band = RadioBand::RadioBandTwoPoint4GHz;
break;
case IeeeFrequencyBand::FiveGHz:
band = RadioBand::RadioBandFiveGHz;
break;
case IeeeFrequencyBand::SixGHz:
band = RadioBand::RadioBandSixGHz;
break;
default:
break;
}

return band;
}

Microsoft::Net::Wifi::Dot11AuthenticationAlgorithm
IeeeAuthenticationAlgorithmToNetRemoteAuthenticationAlgorithm(Microsoft::Net::Wifi::IeeeAuthenticationAlgorithm ieeeAuthenticationAlgorithm)
{
using Microsoft::Net::Wifi::Dot11AuthenticationAlgorithm;
using Microsoft::Net::Wifi::IeeeAuthenticationAlgorithm;

Dot11AuthenticationAlgorithm authenticationAlgorithm{ Dot11AuthenticationAlgorithm::Dot11AuthenticationAlgorithmUnknown };

switch (ieeeAuthenticationAlgorithm) {
case IeeeAuthenticationAlgorithm::OpenSystem:
authenticationAlgorithm = Dot11AuthenticationAlgorithm::Dot11AuthenticationAlgorithmOpen;
break;
case IeeeAuthenticationAlgorithm::SharedKey:
authenticationAlgorithm = Dot11AuthenticationAlgorithm::Dot11AuthenticationAlgorithmSharedKey;
break;
case IeeeAuthenticationAlgorithm::Sae:
authenticationAlgorithm = Dot11AuthenticationAlgorithm::Dot11AuthenticationAlgorithmSae;
break;
// The following cases do not map to Dot11AuthenticationAlgorithm types. This appears to be because the Microsoft
// authentication algorithm definitions do not map 1-1 with the 802.11 definitions. Instead, they more resemble an
// 802.11 AKM. Fixing this requires a breaking API change, so will be deferred.
//
// case IeeeAuthenticationAlgorithm::FastBssTransition:
// case IeeeAuthenticationAlgorithm::Fils:
// case IeeeAuthenticationAlgorithm::FilsPfs:
// case IeeeAuthenticationAlgorithm::FilsPublicKey:
//
default:
break;
}

return authenticationAlgorithm;
}

Microsoft::Net::Wifi::Dot11CipherAlgorithm
IeeeCipherAlgorithmToNetRemoteCipherAlgorithm(Microsoft::Net::Wifi::IeeeCipherSuite ieeeCipherSuite)
{
using Microsoft::Net::Wifi::Dot11CipherAlgorithm;
using Microsoft::Net::Wifi::IeeeCipherSuite;

Dot11CipherAlgorithm cipherAlgorithm{ Dot11CipherAlgorithm::Dot11CipherAlgorithmUnknown };

switch (ieeeCipherSuite) {
case IeeeCipherSuite::Unknown:
cipherAlgorithm = Dot11CipherAlgorithm::Dot11CipherAlgorithmNone;
break;
case IeeeCipherSuite::Wep40:
cipherAlgorithm = Dot11CipherAlgorithm::Dot11CipherAlgorithmWep40;
break;
case IeeeCipherSuite::Tkip:
cipherAlgorithm = Dot11CipherAlgorithm::Dot11CipherAlgorithmTkip;
break;
// case IeeeCipherSuite::Ccmp128: // FIXME
case IeeeCipherSuite::Wep104:
cipherAlgorithm = Dot11CipherAlgorithm::Dot11CipherAlgorithmWep104;
break;
case IeeeCipherSuite::BipCmac128:
cipherAlgorithm = Dot11CipherAlgorithm::Dot11CipherAlgorithmBipCmac128;
break;
case IeeeCipherSuite::Gcmp128:
cipherAlgorithm = Dot11CipherAlgorithm::Dot11CipherAlgorithmGcmp128;
break;
case IeeeCipherSuite::Gcmp256:
cipherAlgorithm = Dot11CipherAlgorithm::Dot11CipherAlgorithmGcmp256;
break;
case IeeeCipherSuite::Ccmp256:
cipherAlgorithm = Dot11CipherAlgorithm::Dot11CipherAlgorithmCcmp256;
break;
case IeeeCipherSuite::BipGmac128:
cipherAlgorithm = Dot11CipherAlgorithm::Dot11CipherAlgorithmBipGmac128;
break;
case IeeeCipherSuite::BipGmac256:
cipherAlgorithm = Dot11CipherAlgorithm::Dot11CipherAlgorithmBipGmac256;
break;
case IeeeCipherSuite::BipCmac256:
cipherAlgorithm = Dot11CipherAlgorithm::Dot11CipherAlgorithmBipCmac256;
break;
default:
break;
}

return cipherAlgorithm;
}

Microsoft::Net::Wifi::AccessPointCapabilities
IeeeAccessPointCapabilitiesToNetRemoteAccessPointCapabilities(const Microsoft::Net::Wifi::Ieee80211AccessPointCapabilities& ieeeCapabilities)
{
using Microsoft::Net::Wifi::AccessPointCapabilities;
using Microsoft::Net::Wifi::Ieee80211AccessPointCapabilities;

AccessPointCapabilities capabilities{};

std::vector<Microsoft::Net::Wifi::Dot11PhyType> phyTypes(std::size(ieeeCapabilities.Protocols));
std::ranges::transform(ieeeCapabilities.Protocols, std::begin(phyTypes), IeeeProtocolToNetRemotePhyType);

*capabilities.mutable_phytypes() = {
std::make_move_iterator(std::begin(phyTypes)),
std::make_move_iterator(std::end(phyTypes))
};

std::vector<Microsoft::Net::Wifi::RadioBand> bands(std::size(ieeeCapabilities.FrequencyBands));
std::ranges::transform(ieeeCapabilities.FrequencyBands, std::begin(bands), IeeeFrequencyBandToNetRemoteRadioBand);

*capabilities.mutable_bands() = {
std::make_move_iterator(std::begin(bands)),
std::make_move_iterator(std::end(bands))
};

std::vector<Microsoft::Net::Wifi::Dot11AuthenticationAlgorithm> authenticationAlgorithms(std::size(ieeeCapabilities.AuthenticationAlgorithms));
std::ranges::transform(ieeeCapabilities.AuthenticationAlgorithms, std::begin(authenticationAlgorithms), IeeeAuthenticationAlgorithmToNetRemoteAuthenticationAlgorithm);

*capabilities.mutable_authenticationalgorithms() = {
std::make_move_iterator(std::begin(authenticationAlgorithms)),
std::make_move_iterator(std::end(authenticationAlgorithms))
};

std::vector<Microsoft::Net::Wifi::Dot11CipherAlgorithm> encryptionAlgorithms(std::size(ieeeCapabilities.EncryptionAlgorithms));
std::ranges::transform(ieeeCapabilities.EncryptionAlgorithms, std::begin(encryptionAlgorithms), IeeeCipherAlgorithmToNetRemoteCipherAlgorithm);

*capabilities.mutable_encryptionalgorithms() = {
std::make_move_iterator(std::begin(encryptionAlgorithms)),
std::make_move_iterator(std::end(encryptionAlgorithms))
};

return capabilities;
}

using Microsoft::Net::Remote::Wifi::WifiEnumerateAccessPointsResultItem;
using Microsoft::Net::Wifi::AccessPointCapabilities;

static constexpr auto AccessPointIdInvalid{ "invalid" };

Microsoft::Net::Remote::Wifi::WifiEnumerateAccessPointsResultItem
MakeInvalidAccessPointResultItem()
{
WifiEnumerateAccessPointsResultItem item{};
item.set_accesspointid(AccessPointIdInvalid);
return item;
}

Microsoft::Net::Remote::Wifi::WifiEnumerateAccessPointsResultItem
IAccessPointToNetRemoteAccessPointResultItem(IAccessPoint& accessPoint)
{
WifiEnumerateAccessPointsResultItem item{};

bool isEnabled{ false };
std::string id{};
Microsoft::Net::Wifi::AccessPointCapabilities capabilities{};

auto interfaceName = accessPoint.GetInterfaceName();
id.assign(std::cbegin(interfaceName), std::cend(interfaceName));

auto accessPointController = accessPoint.CreateController();
if (accessPointController == nullptr) {
LOGE << std::format("Failed to create controller for access point {}", interfaceName);
return MakeInvalidAccessPointResultItem();
}

try {
isEnabled = accessPointController->GetIsEnabled();
} catch (const AccessPointControllerException& apce) {
LOGE << std::format("Failed to get enabled state for access point {} ({})", interfaceName, apce.what());
return MakeInvalidAccessPointResultItem();
}

try {
auto capabilitiesIeee80211 = accessPointController->GetCapabilities();
capabilities = IeeeAccessPointCapabilitiesToNetRemoteAccessPointCapabilities(capabilitiesIeee80211);
} catch (const AccessPointControllerException& apce) {
LOGE << std::format("Failed to get capabilities for access point {} ({})", interfaceName, apce.what());
return MakeInvalidAccessPointResultItem();
}

// Populate the result item.
item.set_accesspointid(std::move(id));
item.set_isenabled(isEnabled);
*item.mutable_capabilities() = std::move(capabilities);

return item;
}

Microsoft::Net::Remote::Wifi::WifiEnumerateAccessPointsResultItem
IAccessPointWeakToNetRemoteAccessPointResultItem(std::weak_ptr<Microsoft::Net::Wifi::IAccessPoint>& accessPointWeak)
{
using Microsoft::Net::Remote::Wifi::WifiEnumerateAccessPointsResultItem;

WifiEnumerateAccessPointsResultItem item{};

auto accessPoint = accessPointWeak.lock();
if (accessPoint != nullptr) {
item = IAccessPointToNetRemoteAccessPointResultItem(*accessPoint.get());
} else {
item = detail::MakeInvalidAccessPointResultItem();
}

return item;
}

bool
NetRemoteAccessPointResultItemIsInvalid(const Microsoft::Net::Remote::Wifi::WifiEnumerateAccessPointsResultItem& item)
{
return (item.accesspointid() == AccessPointIdInvalid);
}
} // namespace detail

::grpc::Status
NetRemoteService::WifiEnumerateAccessPoints([[maybe_unused]] ::grpc::ServerContext* context, [[maybe_unused]] const ::Microsoft::Net::Remote::Wifi::WifiEnumerateAccessPointsRequest* request, ::Microsoft::Net::Remote::Wifi::WifiEnumerateAccessPointsResult* response)
{
using Microsoft::Net::Remote::Wifi::WifiEnumerateAccessPointsResultItem;

LOGD << std::format("Received WifiEnumerateAccessPoints request");

// List all known access points.
auto accessPoints = m_accessPointManager->GetAllAccessPoints();
std::vector<WifiEnumerateAccessPointsResultItem> accessPointResultItems(std::size(accessPoints));
std::ranges::transform(accessPoints, std::begin(accessPointResultItems), [](const auto& accessPointWeak) {
WifiEnumerateAccessPointsResultItem item{};
auto accessPoint = accessPointWeak.lock();
if (accessPoint != nullptr) {
auto interfaceName = accessPoint->GetInterfaceName();
std::string accessPointId{ std::cbegin(interfaceName), std::cend(interfaceName) };
item.set_accesspointid(std::move(accessPointId));
}
return std::move(item);
std::ranges::transform(accessPoints, std::begin(accessPointResultItems), [](auto& accessPointWeak) {
return detail::IAccessPointWeakToNetRemoteAccessPointResultItem(accessPointWeak);
});

// Remove any invalid items.
accessPointResultItems.erase(std::begin(std::ranges::remove_if(accessPointResultItems, detail::NetRemoteAccessPointResultItemIsInvalid)), std::end(accessPointResultItems));

// Update result.
*response->mutable_accesspoints() = {
std::make_move_iterator(std::begin(accessPointResultItems)),
std::make_move_iterator(std::end(accessPointResultItems))
Expand Down
1 change: 1 addition & 0 deletions src/common/tools/cli/NetRemoteCliHandlerOperations.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ NetRemoteCliHandlerOperations::WifiEnumerateAccessPoints()

for (const auto& accessPoint : result.accesspoints()) {
LOGI << std::format(" - [{}]", accessPoint.accesspointid());
LOGI << std::format(" - {}", accessPoint.isenabled() ? "enabled" : "disabled");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <array>
#include <cerrno>
#include <format>
#include <ranges>
#include <stdexcept>
#include <string_view>

Expand Down Expand Up @@ -117,17 +118,47 @@ AccessPointDiscoveryAgentOperationsNetlink::Stop()
}
}

namespace detail
{
/**
* @brief Helper function to determine if an nl80211 interface is an AP. To be used in range expressions.
*
* @param nl80211Interface
* @return true
* @return false
*/
bool
IsNl80211InterfaceTypeAp(const Nl80211Interface &nl80211Interface)
{
return (nl80211Interface.Type == nl80211_iftype::NL80211_IFTYPE_AP);
}

/**
* @brief Helper function returning the name of an nl80211 interface. To be used in range expressions.
*
* @param nl80211Interface
* @return std::string
*/
std::string
Nl80211InterfaceName(const Nl80211Interface &nl80211Interface)
{
return nl80211Interface.Name;
}
} // namespace detail

std::future<std::vector<std::string>>
AccessPointDiscoveryAgentOperationsNetlink::ProbeAsync()
{
std::promise<std::vector<std::string>> probePromise{};
auto probeFuture = probePromise.get_future();

// Enumerate all nl80211 interfaces and filter out those that are not APs.
auto nl80211Interfaces{ Nl80211Interface::Enumerate() };
std::vector<std::string> accessPoints(std::size(nl80211Interfaces));
std::ranges::transform(nl80211Interfaces, std::begin(accessPoints), [](const auto &nl80211Interface) {
return nl80211Interface.Name;
});
auto nl80211ApInterfaceNames = nl80211Interfaces | std::views::filter(detail::IsNl80211InterfaceTypeAp) | std::views::transform(detail::Nl80211InterfaceName);
std::vector<std::string> accessPoints(std::make_move_iterator(std::begin(nl80211ApInterfaceNames)), std::make_move_iterator(std::end(nl80211ApInterfaceNames)));

// Clear the vector since most of the items were moved out.
nl80211Interfaces.clear();

probePromise.set_value(std::move(accessPoints));

Expand Down
Loading