feat(core): add LocalPingScanner for subnet host discovery
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
#include "local_ping_scanner.hpp"
|
||||
#include "logger.hpp"
|
||||
#include <Poco/Net/ICMPClient.h>
|
||||
#include <boost/asio/ip/address_v4.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
LocalPingScanner::LocalPingScanner(
|
||||
const std::string &baseIp, int prefixLen, int timeoutMs
|
||||
)
|
||||
: baseIp_(baseIp)
|
||||
, prefixLen_(prefixLen)
|
||||
, timeoutMs_(timeoutMs)
|
||||
{
|
||||
if (prefixLen_ < 0 || prefixLen_ > 32) {
|
||||
Logger::error("Invalid prefix length in LocalPingScanner constructor");
|
||||
throw std::runtime_error("Bad prefix length");
|
||||
}
|
||||
if (timeoutMs_ <= 0) {
|
||||
timeoutMs_ = 2000;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<std::string> &LocalPingScanner::alive() const
|
||||
{
|
||||
return alive_;
|
||||
}
|
||||
|
||||
uint32_t LocalPingScanner::ipToU32(const std::string &ip)
|
||||
{
|
||||
using namespace boost::asio::ip;
|
||||
return static_cast<uint32_t>(make_address_v4(ip).to_uint());
|
||||
}
|
||||
|
||||
std::string LocalPingScanner::u32ToIp(uint32_t v)
|
||||
{
|
||||
using namespace boost::asio::ip;
|
||||
return address_v4(v).to_string();
|
||||
}
|
||||
|
||||
bool LocalPingScanner::pingHost(const std::string &ip) const
|
||||
{
|
||||
try {
|
||||
return Poco::Net::ICMPClient::pingIPv4(
|
||||
ip,
|
||||
/*repeat=*/1,
|
||||
/*dataSize=*/48,
|
||||
/*ttl=*/64,
|
||||
/*timeout=*/timeoutMs_
|
||||
)
|
||||
> 0;
|
||||
} catch (const std::exception &ex) {
|
||||
Logger::error(
|
||||
std::string("Ping failed for host ") + ip + ": " + ex.what()
|
||||
);
|
||||
return false;
|
||||
} catch (...) {
|
||||
Logger::fatal(std::string("Unknown error pinging host ") + ip);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void LocalPingScanner::runImpl()
|
||||
{
|
||||
alive_.clear();
|
||||
|
||||
const uint32_t base = ipToU32(baseIp_);
|
||||
const uint32_t mask =
|
||||
(prefixLen_ == 0) ? 0u : (0xFFFFFFFFu << (32 - prefixLen_));
|
||||
const uint32_t network = base & mask;
|
||||
const uint32_t broadcast = network | (~mask);
|
||||
|
||||
// No hosts when prefix is too tight (e.g., /32).
|
||||
if (broadcast - network <= 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string selfIp = u32ToIp(base);
|
||||
|
||||
for (uint32_t v = network + 1; v < broadcast; ++v) {
|
||||
const std::string ipStr = u32ToIp(v);
|
||||
|
||||
if (ipStr == selfIp) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pingHost(ipStr)) {
|
||||
alive_.push_back(ipStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocalPingScanner::run()
|
||||
{
|
||||
try {
|
||||
runImpl();
|
||||
} catch (const std::exception &ex) {
|
||||
Logger::error(
|
||||
std::string("Exception in LocalPingScanner::run: ") + ex.what()
|
||||
);
|
||||
} catch (...) {
|
||||
Logger::fatal("Unknown exception in LocalPingScanner::run");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class LocalPingScanner
|
||||
{
|
||||
public:
|
||||
LocalPingScanner(
|
||||
const std::string &baseIp, int prefixLen, int timeoutMs = 2000
|
||||
);
|
||||
|
||||
void run(); // blocking scan over subnet
|
||||
const std::vector<std::string> &alive() const;
|
||||
|
||||
private:
|
||||
static std::string u32ToIp(uint32_t v);
|
||||
static uint32_t ipToU32(const std::string &ip);
|
||||
bool pingHost(const std::string &ip) const;
|
||||
void runImpl();
|
||||
|
||||
std::string baseIp_;
|
||||
int prefixLen_;
|
||||
int timeoutMs_;
|
||||
std::vector<std::string> alive_;
|
||||
};
|
||||
Reference in New Issue
Block a user