Skip to content

Commit

Permalink
Add more ICMP ping options
Browse files Browse the repository at this point in the history
Add support for df (Don't fragment) option, size, timeout.

Signed-off-by: Denys Fedoryshchenko <[email protected]>
  • Loading branch information
nuclearcat committed Jul 8, 2024
1 parent 3ae2ef7 commit 90038f3
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 30 deletions.
10 changes: 9 additions & 1 deletion manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,18 @@ The checker will actually delete messages called 'Simplomon test message' to pre
## ping
Send out IPv4, IPv6 ping messages. Supports %-style link selection for fe80 usage.

Parameters:
* df: true/false, if true(default), the checker will also check if the network path has MTU limitations. Ignored for IPv6.
* timeout: double, in seconds, how long to wait for a ping reply. Default is 1 second.
* size: integer, how many bytes to send, additional to icmp header. Default is 1016 bytes.

```lua
ping{servers={"9.9.9.9", "8.8.8.8"}} -- does our network even work
ping{servers={"10.0.252.2"}, size=1472} -- do we have full MTU 1500 till this server?
ping{servers={"fe80::%enp4s0"}} -- does our local network work
ping{servers={"2a03:2880:f142:182:face:b00c:0:25de"}} -- ping IPv6 facebook
```
TBC


## prometheusExp
Query a Prometheus Node Exporter. TBC.
Expand Down
82 changes: 53 additions & 29 deletions netmon.cc
Original file line number Diff line number Diff line change
Expand Up @@ -336,12 +336,11 @@ CheckResult HTTPRedirChecker::perform()
}


#define PACKETSIZE 1024
namespace {
struct icmppacket
{
struct icmphdr hdr;
char msg[PACKETSIZE-sizeof(struct icmphdr)];
char msg[];
};
}
/*--------------------------------------------------------------------*/
Expand All @@ -362,35 +361,43 @@ static unsigned short internetchecksum(void *b, int len)
return result;
}

static std::string makeICMPQuery(int family, uint16_t id, uint16_t seq)
static std::string makeICMPQuery(int family, uint16_t id, uint16_t seq, size_t psize)
{
if(family==AF_INET) {
icmppacket p;
memset(&p, 0, sizeof(p));
p.hdr.type = ICMP_ECHO;
p.hdr.un.echo.id = id;
p.hdr.un.echo.sequence = seq;
size_t full_size = sizeof(icmphdr) + psize;
vector<char> store(full_size, 0);
icmppacket *p = (icmppacket *)store.data();

p->hdr.type = ICMP_ECHO;
p->hdr.un.echo.id = id;
p->hdr.un.echo.sequence = seq;
unsigned int i;
for(i = 0; i < sizeof(p.msg)-1; i++ )
p.msg[i] = i+'0';
p.msg[i] = 0;


p.hdr.checksum = 0;
p.hdr.checksum = internetchecksum(&p, sizeof(p));
return std::string((const char*)&p, sizeof(p));
for(i = 0; i < psize; i++) {
p->msg[i] = (char)i;
}

p->hdr.checksum = 0;
p->hdr.checksum = internetchecksum(p, full_size);
return std::string((const char*)p, full_size);
}
else {
/* compose ICMPv6 packet */
struct icmp6_hdr hdr;
memset(&hdr, 0, sizeof(hdr));

hdr.icmp6_type = ICMP6_ECHO_REQUEST;
hdr.icmp6_code = 0;
hdr.icmp6_dataun.icmp6_un_data16[0] = id; /* identifier */
hdr.icmp6_dataun.icmp6_un_data16[1] = seq; /* sequence no */

return std::string((const char*)&hdr, sizeof(hdr));
size_t full_size = sizeof(struct icmp6_hdr) + psize;
vector<char> store(full_size, 0);
void *packet = store.data();
struct icmp6_hdr *hdr = (struct icmp6_hdr *)packet;

hdr->icmp6_type = ICMP6_ECHO_REQUEST;
hdr->icmp6_code = 0;
hdr->icmp6_dataun.icmp6_un_data16[0] = id; /* identifier */
hdr->icmp6_dataun.icmp6_un_data16[1] = seq; /* sequence no */

/* fill the rest of the packet */
unsigned char *data = (unsigned char *)(hdr + 1);
for (size_t i = 0; i < psize; i++)
data[i] = i;

return std::string((const char*)hdr, full_size);
}
}

Expand Down Expand Up @@ -437,7 +444,7 @@ bool HarvestTTL(struct msghdr* msgh, int* ttl)

PINGChecker::PINGChecker(sol::table data) : Checker(data, 2)
{
checkLuaTable(data, {"servers"}, {"localIP"});
checkLuaTable(data, {"servers"}, {"localIP", "timeout", "size", "df"});
for(const auto& s: data.get<vector<string>>("servers")) {
d_servers.insert(ComboAddress(s));
}
Expand All @@ -447,6 +454,18 @@ PINGChecker::PINGChecker(sol::table data) : Checker(data, 2)
d_attributes["localIP"] = d_localIP->toString();
}

d_timeout = data.get_or("timeout", 1.0);
if (d_timeout <= 0 || d_timeout > 10)
throw runtime_error("ping timeout must be reasonable, between 0 and 10 seconds");

/* Size of payload, except IP/ICMP header, 1016 to imitate previous default */
d_size = data.get_or("size", 1016);
if(d_size < 0 || d_size > 65500)
throw runtime_error("ping size must be between 0 and 65500");

/* Based on observation, old default was DF is set */
d_dontFragment = data.get_or("df", true);

try {
Socket sock(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
}
Expand All @@ -467,13 +486,18 @@ CheckResult PINGChecker::perform()

SConnect(sock, s);
SSetsockopt(sock, SOL_IP, IP_RECVTTL, 1);
if (d_dontFragment) {
SSetsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DO);
} else {
SSetsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
}

string packet = makeICMPQuery(s.sin4.sin_family, 1, 1);
string packet = makeICMPQuery(s.sin4.sin_family, 1, 1, d_size);
DTime dt;
dt.start();
SWrite(sock, packet);
double timeo=1.0;
if(!waitForData(sock, &timeo)) { // timeout

if(!waitForData(sock, &d_timeout)) {
ret.d_reasons[s.toStringWithPort()].push_back(fmt::format("Timeout waiting for ping response from {}",
s.toString()));
continue;
Expand Down
3 changes: 3 additions & 0 deletions simplomon.hh
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ public:
private:
std::set<ComboAddress> d_servers;
std::optional<ComboAddress> d_localIP;
double d_timeout;
size_t d_size;
bool d_dontFragment;
};


Expand Down

0 comments on commit 90038f3

Please sign in to comment.