[monitoring-plugins] check_icmp: validate source address of echo ...
GitHub
git at monitoring-plugins.org
Mon Oct 19 17:30:14 CEST 2026
Module: monitoring-plugins
Branch: master
Commit: c3939fea8bb60625914b3661368820e09acfa263
Author: Timotheus Bachinger <Timotheus.bachinger at checkmk.com>
Committer: GitHub <noreply at github.com>
Date: Mon Oct 19 17:22:24 2026 +0200
URL: https://www.monitoring-plugins.org/repositories/monitoring-plugins/commit/?id=c3939fea
check_icmp: validate source address of echo replies (#2366)
Echo replies are matched to a target only by icmp_id and icmp_seq. The
icmp_id is the low 16 bits of the PID, so with pid_max > 65536 another
concurrently running pinger (a second check_icmp or any other ping tool)
can use the same id. Since every raw ICMP socket on the machine receives
a copy of all echo replies, such foreign replies were accepted and
counted for whichever target the sequence number pointed to, causing
wrong RTA/packet loss values.
Drop echo replies whose source address does not match the address of the
target selected by the sequence number.
---
plugins-root/check_icmp.c | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c
index 4b1e5003..d7b94477 100644
--- a/plugins-root/check_icmp.c
+++ b/plugins-root/check_icmp.c
@@ -165,6 +165,8 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad
time_t *target_interval, uint16_t sender_id, ping_target **table,
unsigned short packets, unsigned short number_of_targets,
check_icmp_state *program_state);
+static bool reply_address_matches_target(const struct sockaddr_storage *resp_addr,
+ const ping_target *target);
/* Sending data */
static int send_icmp_ping(check_icmp_socket_set sockset, ping_target *host,
@@ -1237,6 +1239,21 @@ static int wait_for_reply(check_icmp_socket_set sockset, const time_t time_inter
target = table[ntohs(packet.icp6->icmp6_seq) / packets];
}
+ /* The reply matches our icmp_id, but that id is only the low 16 bits
+ * of our PID. With pid_max > 65536 a concurrently running pinger
+ * (another check_icmp or some other ping tool) may use the same id,
+ * and every raw ICMP socket on the machine receives a copy of all
+ * echo replies. So drop the reply unless it actually comes from the
+ * target we pinged, otherwise it would be counted for the wrong target. */
+ if (!reply_address_matches_target(&resp_addr, target)) {
+ if (debug) {
+ char address[INET6_ADDRSTRLEN];
+ parse_address(&resp_addr, address, sizeof(address));
+ printf("dropping reply with our icmp_id from unexpected address %s\n", address);
+ }
+ continue;
+ }
+
time_t tdiff = get_timevaldiff(data.stime, packet_received_timestamp);
if (target->last_tdiff > 0) {
@@ -1308,6 +1325,22 @@ static int wait_for_reply(check_icmp_socket_set sockset, const time_t time_inter
return 0;
}
+static bool reply_address_matches_target(const struct sockaddr_storage *resp_addr,
+ const ping_target *target) {
+ if (resp_addr->ss_family != target->address.ss_family) {
+ return false;
+ }
+
+ if (resp_addr->ss_family == AF_INET) {
+ return ((const struct sockaddr_in *)resp_addr)->sin_addr.s_addr ==
+ ((const struct sockaddr_in *)&target->address)->sin_addr.s_addr;
+ }
+
+ return memcmp(&((const struct sockaddr_in6 *)resp_addr)->sin6_addr,
+ &((const struct sockaddr_in6 *)&target->address)->sin6_addr,
+ sizeof(struct in6_addr)) == 0;
+}
+
/* the ping functions */
static int send_icmp_ping(const check_icmp_socket_set sockset, ping_target *host,
const unsigned short icmp_pkt_size, const uint16_t sender_id,
More information about the Commits
mailing list