blob: f9295672ed82df31edf872812685b903d684c549 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "utils/net/NetworkInterfaceInfo.h"
#include "utils/net/Socket.h"
#include "core/logging/LoggerFactory.h"
#ifdef WIN32
#include <iphlpapi.h>
#pragma comment(lib, "IPHLPAPI.lib")
#include "utils/OsUtils.h"
#include "utils/UnicodeConversion.h"
#else
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#endif
namespace org::apache::nifi::minifi::utils {
std::shared_ptr<core::logging::Logger> NetworkInterfaceInfo::logger_ = core::logging::LoggerFactory<NetworkInterfaceInfo>::getLogger();
#ifdef WIN32
NetworkInterfaceInfo::NetworkInterfaceInfo(const IP_ADAPTER_ADDRESSES* adapter)
: name_(to_string(adapter->FriendlyName)),
running_(adapter->OperStatus == IfOperStatusUp),
loopback_(adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK) {
for (auto unicast_address = adapter->FirstUnicastAddress; unicast_address != nullptr; unicast_address = unicast_address->Next) {
if (unicast_address->Address.lpSockaddr->sa_family == AF_INET) {
ip_v4_addresses_.push_back(net::sockaddr_ntop(unicast_address->Address.lpSockaddr));
} else if (unicast_address->Address.lpSockaddr->sa_family == AF_INET6) {
ip_v6_addresses_.push_back(net::sockaddr_ntop(unicast_address->Address.lpSockaddr));
}
}
}
#else
NetworkInterfaceInfo::NetworkInterfaceInfo(const struct ifaddrs* ifa)
: name_(ifa->ifa_name),
running_(ifa->ifa_flags & IFF_RUNNING),
loopback_(ifa->ifa_flags & IFF_LOOPBACK) {
if (ifa->ifa_addr->sa_family == AF_INET) {
ip_v4_addresses_.push_back(net::sockaddr_ntop(ifa->ifa_addr));
} else if (ifa->ifa_addr->sa_family == AF_INET6) {
ip_v6_addresses_.push_back(net::sockaddr_ntop(ifa->ifa_addr));
}
}
#endif
namespace {
struct HasName {
explicit HasName(const std::string& name) : name_(name) {}
bool operator()(const NetworkInterfaceInfo& network_interface_info) {
return network_interface_info.getName() == name_;
}
const std::string& name_;
};
}
std::vector<NetworkInterfaceInfo> NetworkInterfaceInfo::getNetworkInterfaceInfos(const std::function<bool(const NetworkInterfaceInfo&)>& filter,
const std::optional<uint32_t> max_interfaces) {
std::vector<NetworkInterfaceInfo> network_adapters;
#ifdef WIN32
ULONG buffer_length = sizeof(IP_ADAPTER_ADDRESSES);
auto get_adapters_err = GetAdaptersAddresses(0, 0, nullptr, nullptr, &buffer_length);
if (ERROR_BUFFER_OVERFLOW != get_adapters_err) {
logger_->log_error("GetAdaptersAddresses failed: {}", get_adapters_err);
return network_adapters;
}
std::vector<char> bytes(buffer_length, 0);
auto* adapter = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(bytes.data());
get_adapters_err = GetAdaptersAddresses(0, 0, nullptr, adapter, &buffer_length);
if (NO_ERROR != get_adapters_err) {
logger_->log_error("GetAdaptersAddresses failed: {}", get_adapters_err);
return network_adapters;
}
while (adapter != nullptr) {
NetworkInterfaceInfo interface_info(adapter);
if (filter(interface_info)) {
auto it = std::find_if(network_adapters.begin(), network_adapters.end(), HasName(interface_info.getName()));
if (it == network_adapters.end()) {
network_adapters.emplace_back(std::move(interface_info));
} else {
interface_info.moveAddressesInto(*it);
}
}
if (max_interfaces.has_value() && network_adapters.size() >= max_interfaces.value())
return network_adapters;
adapter = adapter->Next;
}
#else
struct ifaddrs* interface_addresses = nullptr;
auto cleanup = gsl::finally([&interface_addresses] { freeifaddrs(interface_addresses); });
if (getifaddrs(&interface_addresses) == -1) {
logger_->log_error("getifaddrs failed: {}", std::strerror(errno));
return network_adapters;
}
for (struct ifaddrs* ifa = interface_addresses; ifa != nullptr; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr)
continue;
NetworkInterfaceInfo interface_info(ifa);
if (filter(interface_info)) {
auto it = std::find_if(network_adapters.begin(), network_adapters.end(), HasName(interface_info.getName()));
if (it == network_adapters.end()) {
network_adapters.emplace_back(std::move(interface_info));
} else {
interface_info.moveAddressesInto(*it);
}
}
if (max_interfaces.has_value() && network_adapters.size() >= max_interfaces.value())
return network_adapters;
}
#endif
return network_adapters;
}
namespace {
void move_append(std::vector<std::string>& source, std::vector<std::string>& destination) {
destination.reserve(destination.size() + source.size());
std::move(std::begin(source), std::end(source), std::back_inserter(destination));
source.clear();
}
} // namespace
void NetworkInterfaceInfo::moveAddressesInto(NetworkInterfaceInfo& destination) {
move_append(ip_v4_addresses_, destination.ip_v4_addresses_);
move_append(ip_v6_addresses_, destination.ip_v6_addresses_);
}
} // namespace org::apache::nifi::minifi::utils