[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