4#include "Stroika/Frameworks/StroikaPreComp.h"
8#include "Stroika/Foundation/Containers/Collection.h"
10#include "Stroika/Foundation/Execution/Timeout.h"
11#include "Stroika/Foundation/IO/Network/InternetProtocol/ICMP.h"
12#include "Stroika/Foundation/IO/Network/InternetProtocol/IP.h"
14#include "Stroika/Foundation/IO/Network/SocketAddress.h"
24using namespace Stroika::Foundation::Debug;
26using namespace Stroika::Foundation::Memory;
27using namespace Stroika::Foundation::IO;
29using namespace Stroika::Foundation::IO::Network::InternetProtocol;
31using namespace Stroika::Frameworks;
32using namespace Stroika::Frameworks::NetworkMonitor;
33using namespace Stroika::Frameworks::NetworkMonitor::Ping;
43String Ping::Options::ToString ()
const
47 sb <<
"maxHops: "sv << fMaxHops;
48 sb <<
", timeout: "sv << fTimeout;
49 sb <<
", packetPayloadSize: "sv << fPacketPayloadSize;
59String Pinger::ResultType::ToString ()
const
63 sb <<
"pingTime: "sv << fPingTime;
64 sb <<
", hopCount: "sv << fHopCount;
77 , fICMPPacketSize_{Options::kAllowedICMPPayloadSizeRange.Pin (options.fPacketPayloadSize) + sizeof (ICMP::V4::PacketHeader)}
78 , fSendPacket_{fICMPPacketSize_}
79 , fSocket_{IO::Network::ConnectionlessSocket::New (
SocketAddress::INET, Socket::RAW, IPPROTO_ICMP)}
80 , fNextSequenceNumber_{static_cast<uint16_t> (fAllUInt16Distribution_ (fRng_))}
81 , fPingTimeout_{options.fTimeout}
83 Debug::TraceContextBumper ctx{
"Frameworks::NetworkMonitor::Ping::Pinger::CTOR",
"addr={}, options={}"_f, fDestination_, fOptions_};
85 for (
byte* p = (
byte*)fSendPacket_.begin () + sizeof (ICMP::V4::PacketHeader); p < fSendPacket_.end (); ++p) {
86 uniform_int_distribution<mt19937::result_type> anyByteDistribution (0, numeric_limits<uint8_t>::max ());
87 *p =
static_cast<byte> (anyByteDistribution (fRng_));
94 return RunOnce_ICMP_ (ttl.value_or (fOptions_.fMaxHops));
97Pinger::ResultType Pinger::RunOnce_ICMP_ (
unsigned int ttl)
101 fSocket_.
setsockopt (IPPROTO_IP, IP_TTL, ttl);
103 ICMP::V4::PacketHeader pingRequest = [&] () {
104 ICMP::V4::PacketHeader tmp{};
105 tmp.type = ICMP::V4::ICMP_ECHO_REQUEST;
106 tmp.id =
static_cast<uint16_t
> (fAllUInt16Distribution_ (fRng_));
107 tmp.seq = ++fNextSequenceNumber_;
108 tmp.timestamp =
static_cast<uint32_t
> (Time::GetTickCount ().time_since_epoch ().count () * 1000);
111 (void)::memcpy (fSendPacket_.begin (), &pingRequest,
sizeof (pingRequest));
112 reinterpret_cast<ICMP::V4::PacketHeader*
> (fSendPacket_.begin ())->checksum =
113 IP::ip_checksum (fSendPacket_.begin (), fSendPacket_.begin () + fICMPPacketSize_);
121 constexpr size_t kExtraSluff_{100};
122 StackBuffer<byte> recv_buf{Memory::eUninitialized, fICMPPacketSize_ +
sizeof (ICMP::V4::PacketHeader) + 2 *
sizeof (IP::V4::PacketHeader) +
124 size_t n = fSocket_.
ReceiveFrom (recv_buf, 0, &fromAddress, fPingTimeout_).size ();
125#if USE_NOISY_TRACE_IN_THIS_MODULE_
126 DbgTrace (
"got back packet from {}"_f, fromAddress);
128 const IP::V4::PacketHeader* replyIPHeader =
reinterpret_cast<const IP::V4::PacketHeader*
> (recv_buf.begin ());
131 unsigned short header_len = replyIPHeader->ihl * 4;
132 const ICMP::V4::PacketHeader* replyICMPHeader = (
const ICMP::V4::PacketHeader*)((
const byte*)replyIPHeader + header_len);
135 if (n < header_len + ICMP::V4::ICMP_MIN) {
142 optional<uint16_t> echoedID;
143 switch (replyICMPHeader->type) {
144 case ICMP::V4::ICMP_ECHO_REPLY: {
145 echoedID = replyICMPHeader->id;
147 case ICMP::V4::ICMP_TTL_EXPIRE: {
149 const ICMP::V4::PacketHeader* echoedICMPHeader =
150 (
const ICMP::V4::PacketHeader*)((
const byte*)replyICMPHeader + 8 +
sizeof (IP::V4::PacketHeader));
151 echoedID = echoedICMPHeader->id;
153 case ICMP::V4::ICMP_DEST_UNREACH: {
155 const ICMP::V4::PacketHeader* echoedICMPHeader =
156 (
const ICMP::V4::PacketHeader*)((
const byte*)replyICMPHeader + 8 +
sizeof (IP::V4::PacketHeader));
157 echoedID = echoedICMPHeader->id;
160 if (echoedID and echoedID != pingRequest.id) {
161 DbgTrace (
"echoedID ({} != pingRequest.id (0x{:x})) so ignoring this reply"_f, echoedID,
static_cast<int> (pingRequest.id));
169 switch (replyICMPHeader->type) {
170 case ICMP::V4::ICMP_ECHO_REPLY: {
174 unsigned int nHops{};
175 if (replyIPHeader->ttl > 128) {
176 nHops = 257 - replyIPHeader->ttl;
178 else if (replyIPHeader->ttl > 64) {
179 nHops = 129 - replyIPHeader->ttl;
182 nHops = 65 - replyIPHeader->ttl;
184#if USE_NOISY_TRACE_IN_THIS_MODULE_
185 DbgTrace (
"reply->ttl = {}, nHops = {}"_f, replyIPHeader->ttl, nHops);
187 return ResultType{
Duration{Time::GetTickCount ().time_since_epoch ().count () - replyICMPHeader->timestamp / 1000.0}, nHops};
189 case ICMP::V4::ICMP_TTL_EXPIRE: {
192 case ICMP::V4::ICMP_DEST_UNREACH: {
213 sb <<
"interval: "sv << fInterval;
214 sb <<
", count: "sv << fSampleCount;
224String SampleResults::ToString ()
const
228 if (fMedianPingTime) {
229 sb <<
"medianPingTime: "sv << *fMedianPingTime;
231 if (fMedianHopCount) {
232 sb <<
", medianHopCount: "sv << fMedianHopCount;
234 if (fExceptionCount != 0) {
235 sb <<
", exceptionCount: "sv << fExceptionCount;
246SampleResults NetworkMonitor::Ping::Sample (
const InternetAddress& addr,
const SampleOptions& sampleOptions,
const Options& options)
248 Debug::TraceContextBumper ctx{
"Frameworks::NetworkMonitor::Ping::Sample",
"addr={}, sampleOptions={}, options={}"_f, addr, sampleOptions, options};
249 Pinger pinger{addr, options};
252 unsigned int samplesTaken{};
253 while (samplesTaken < sampleOptions.fSampleCount) {
254 if (samplesTaken != 0) {
258 Pinger::ResultType tmp = pinger.RunOnce ();
259 sampleTimes += tmp.fPingTime;
260 sampleHopCounts += tmp.fHopCount;
267 return sampleTimes.
empty () ? SampleResults{{}, {}, samplesTaken}
269 static_cast<unsigned int> (samplesTaken - sampleTimes.
size ())};
#define AssertNotReached()
time_point< RealtimeClock, DurationSeconds > TimePointSeconds
TimePointSeconds is a simpler approach to chrono::time_point, which doesn't require using templates e...
#define Stroika_Foundation_Debug_OptionalizeTraceArgs(...)
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
Exception<> is a replacement (subclass) for any std c++ exception class (e.g. the default 'std::excep...
nonvirtual void SendTo(span< const byte > data, const SocketAddress &sockAddr) const
nonvirtual span< byte > ReceiveFrom(span< byte > into, int flag, SocketAddress *fromAddress, Time::DurationSeconds timeout=Time::kInfinity) const
nonvirtual void setsockopt(int level, int optname, ARG_TYPE arg) const
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
Duration is a chrono::duration<double> (=.
nonvirtual size_t size() const
Returns the number of items contained.
nonvirtual optional< RESULT_TYPE > Median(const INORDER_COMPARE_FUNCTION &compare={}) const
nonvirtual bool empty() const
Returns true iff size() == 0.
nonvirtual ResultType RunOnce(const optional< unsigned int > &ttl={})
run the Ping () operation one time, and return the (timing) results.
String ToString(T &&t, ARGS... args)
Return a debug-friendly, display version of the argument: not guaranteed parsable or usable except fo...
void Sleep(Time::Duration seconds2Wait)
void ThrowTimeoutExceptionAfter(Time::TimePointSeconds afterTickCount, EXCEPTION &&exception2Throw)
How Stroika reports a timeout.
void Throw(T &&e2Throw)
like the builtin C++ 'throw', but stamps in the current Activity context and does helpful,...