Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add feature: support ICMP type 13/14 timestamps #353

Merged
merged 7 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion ci/test-04-options-a-b.pl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/perl -w

use Test::Command tests => 41;
use Test::Command tests => 47;
use Test::More;
use Time::HiRes qw(gettimeofday tv_interval);

Expand Down Expand Up @@ -84,6 +84,17 @@
$cmd->stderr_is_eq("");
}

# fping --icmp-timestamp
SKIP: {
if($^O eq 'darwin') {
skip 'On macOS, this test is unreliable', 3;
}
my $cmd = Test::Command->new(cmd => "fping --icmp-timestamp 127.0.0.1");
$cmd->exit_is_num(0);
$cmd->stdout_like(qr{127\.0\.0\.1 is alive \(Timestamp Originate=\d+ Receive=\d+ Transmit=\d+\)});
$cmd->stderr_is_eq("");
}

# fping --print-ttl with IPv6
SKIP: {
if($ENV{SKIP_IPV6}) {
Expand Down Expand Up @@ -111,6 +122,14 @@
$cmd->stderr_is_eq("");
}

# fping --icmp-timestamp -b
{
my $cmd = Test::Command->new(cmd => "fping --icmp-timestamp -b 1000 127.0.0.1");
$cmd->exit_is_num(1);
$cmd->stdout_is_eq("");
$cmd->stderr_like(qr{Usage:});
}

# fping -B
SKIP: {
if($^O eq 'darwin') {
Expand Down
17 changes: 16 additions & 1 deletion ci/test-05-options-c-e.pl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/perl -w

use Test::Command tests => 75;
use Test::Command tests => 78;
use Test::More;

# -c n count of pings to send to each target (default 1)
Expand Down Expand Up @@ -77,6 +77,21 @@
ff02::1 : xmt/rcv/%loss = 1/1/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+\n});
}

# fping --icmp-timestamp -c n 127.0.0.1
SKIP: {
if($^O eq 'darwin') {
skip 'On macOS, this test is unreliable', 3;
}
my $cmd = Test::Command->new(cmd => "fping -4 --icmp-timestamp -c 2 127.0.0.1");
$cmd->exit_is_num(0);
$cmd->stdout_like(qr{127\.0\.0\.1 : \[0\], 20 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\), ICMP timestamp: Originate=\d+ Receive=\d+ Transmit=\d+
127\.0\.0\.1 : \[1\], 20 bytes, \d\.\d+ ms \(\d\.\d+ avg, 0% loss\), ICMP timestamp: Originate=\d+ Receive=\d+ Transmit=\d+
});

$cmd->stderr_like(qr{127\.0\.0\.1 : xmt/rcv/%loss = 2/2/0%, min/avg/max = \d\.\d+/\d\.\d+/\d\.\d+
});
}

# fping -C n
{
my $cmd = Test::Command->new(cmd => "fping -4 -C 2 -p 100 localhost 127.0.0.1");
Expand Down
6 changes: 6 additions & 0 deletions doc/fping.pod
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ to any target (default is 10, minimum is 1).

Set the interface (requires SO_BINDTODEVICE support).

=item B<--icmp-timestamp>

Send ICMP timestamp requests (ICMP type 13) instead of ICMP Echo requests.
Cannot be used together with B<-b> because ICMP timestamp messages have a fixed size.
IPv4 only, requires root privileges.
gsnw-sebast marked this conversation as resolved.
Show resolved Hide resolved

=item B<-k>, B<--fwmark>=I<FWMARK>

Set FWMARK on ping packets for policy-based routing. Requires Linux kernel
Expand Down
54 changes: 49 additions & 5 deletions src/fping.c
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ int timestamp_format_flag = 0;
int random_data_flag = 0;
int cumulative_stats_flag = 0;
int check_source_flag = 0;
int icmp_request_typ = 0;
int print_tos_flag = 0;
int print_ttl_flag = 0;
#if defined(DEBUG) || defined(_DEBUG)
Expand Down Expand Up @@ -536,6 +537,7 @@ int main(int argc, char **argv)
{ "ttl", 'H', OPTPARSE_REQUIRED },
{ "interval", 'i', OPTPARSE_REQUIRED },
{ "iface", 'I', OPTPARSE_REQUIRED },
{ "icmp-timestamp", '0', OPTPARSE_NONE },
#ifdef SO_MARK
{ "fwmark", 'k', OPTPARSE_REQUIRED },
#endif
Expand Down Expand Up @@ -584,6 +586,8 @@ int main(int argc, char **argv)
}
} else if (strstr(optparse_state.optlongname, "check-source") != NULL) {
check_source_flag = 1;
} else if (strstr(optparse_state.optlongname, "icmp-timestamp") != NULL) {
icmp_request_typ = 13;
} else if (strstr(optparse_state.optlongname, "print-tos") != NULL) {
print_tos_flag = 1;
} else if (strstr(optparse_state.optlongname, "print-ttl") != NULL) {
Expand Down Expand Up @@ -687,6 +691,8 @@ int main(int argc, char **argv)
case 'b':
if (sscanf(optparse_state.optarg, "%u", &ping_data_size) != 1)
usage(1);
if (icmp_request_typ > 0)
usage(1);

break;

Expand Down Expand Up @@ -1925,6 +1931,7 @@ int send_ping(HOST_ENTRY *h, int index)
int n;
int myseq;
int ret = 1;
uint8_t proto = ICMP_ECHO;

update_current_time();
h->last_send_time = current_time_ns;
Expand All @@ -1933,7 +1940,9 @@ int send_ping(HOST_ENTRY *h, int index)
dbg_printf("%s [%d]: send ping\n", h->host, index);

if (h->saddr.ss_family == AF_INET && socket4 >= 0) {
n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4);
if(icmp_request_typ == 13)
proto = ICMP_TSTAMP;
n = socket_sendto_ping_ipv4(socket4, (struct sockaddr *)&h->saddr, h->saddr_len, myseq, ident4, proto);
}
#ifdef IPV6
else if (h->saddr.ss_family == AF_INET6 && socket6 >= 0) {
Expand Down Expand Up @@ -2173,7 +2182,10 @@ int decode_icmp_ipv4(
unsigned short *id,
unsigned short *seq,
int *ip_header_tos,
int *ip_header_ttl)
int *ip_header_ttl,
uint32_t *ip_header_otime_ms,
uint32_t *ip_header_rtime_ms,
uint32_t *ip_header_ttime_ms)
{
struct icmp *icp;
int hlen = 0;
Expand Down Expand Up @@ -2206,7 +2218,7 @@ int decode_icmp_ipv4(

icp = (struct icmp *)(reply_buf + hlen);

if (icp->icmp_type != ICMP_ECHOREPLY) {
if (icp->icmp_type != ICMP_ECHOREPLY && icp->icmp_type != ICMP_TSTAMPREPLY) {
/* Handle other ICMP packets */
struct icmp *sent_icmp;
SEQMAP_VALUE *seqmap_value;
Expand All @@ -2221,7 +2233,7 @@ int decode_icmp_ipv4(

sent_icmp = (struct icmp *)(reply_buf + hlen + ICMP_MINLEN + sizeof(struct ip));

if (sent_icmp->icmp_type != ICMP_ECHO || sent_icmp->icmp_id != ident4) {
if ((sent_icmp->icmp_type != ICMP_ECHO && sent_icmp->icmp_type != ICMP_TSTAMP) || sent_icmp->icmp_id != ident4) {
/* not caused by us */
return -1;
}
Expand Down Expand Up @@ -2272,6 +2284,22 @@ int decode_icmp_ipv4(

*id = icp->icmp_id;
*seq = ntohs(icp->icmp_seq);
if(icp->icmp_type == ICMP_TSTAMPREPLY) {
gsnw-sebast marked this conversation as resolved.
Show resolved Hide resolved

/* Check that reply_buf_len is sufficiently big to contain the timestamps */
if (reply_buf_len < hlen + ICMP_MINLEN + 3 * sizeof(uint32_t)) {
if (verbose_flag) {
char buf[INET6_ADDRSTRLEN];
getnameinfo(response_addr, response_addr_len, buf, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
printf("received packet too short for ICMP Timestamp Reply (%d bytes from %s)\n", (int)reply_buf_len, buf);
}
return -1;
}

*ip_header_otime_ms = ntohl(icp->icmp_dun.id_ts.its_otime);
*ip_header_rtime_ms = ntohl(icp->icmp_dun.id_ts.its_rtime);
*ip_header_ttime_ms = ntohl(icp->icmp_dun.id_ts.its_ttime);
}

return hlen;
}
Expand Down Expand Up @@ -2384,6 +2412,10 @@ int wait_for_reply(int64_t wait_time)
unsigned short seq;
int ip_header_tos = -1;
int ip_header_ttl = -1;
// ICMP Timestamp
uint32_t ip_header_otime_ms;
uint32_t ip_header_rtime_ms;
uint32_t ip_header_ttime_ms;

/* Receive packet */
result = receive_packet(wait_time, /* max. wait time, in ns */
Expand Down Expand Up @@ -2412,7 +2444,10 @@ int wait_for_reply(int64_t wait_time)
&id,
&seq,
&ip_header_tos,
&ip_header_ttl);
&ip_header_ttl,
&ip_header_otime_ms,
&ip_header_rtime_ms,
&ip_header_ttime_ms);
if (ip_hlen < 0) {
return 1;
}
Expand Down Expand Up @@ -2539,6 +2574,10 @@ int wait_for_reply(int64_t wait_time)
}
}

if (icmp_request_typ == 13) {
printf(" (Timestamp Originate=%u Receive=%u Transmit=%u)", ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms);
}

if (elapsed_flag)
printf(" (%s ms)", sprint_tm(this_reply));

Expand Down Expand Up @@ -2577,6 +2616,10 @@ int wait_for_reply(int64_t wait_time)
fprintf(stderr, " [<- %s]", buf);
}

if (icmp_request_typ == 13) {
printf(", ICMP timestamp: Originate=%u Receive=%u Transmit=%u", ip_header_otime_ms, ip_header_rtime_ms, ip_header_ttime_ms);
}

printf("\n");
}

Expand Down Expand Up @@ -3088,6 +3131,7 @@ void usage(int is_error)
fprintf(out, " -t, --timeout=MSEC individual target initial timeout (default: %.0f ms,\n", timeout / 1e6);
fprintf(out, " except with -l/-c/-C, where it's the -p period up to 2000 ms)\n");
fprintf(out, " --check-source discard replies not from target address\n");
fprintf(out, " --icmp-timestamp use ICMP Timestamp instead of ICMP Echo\n");
fprintf(out, "\n");
fprintf(out, "Output options:\n");
fprintf(out, " -a, --alive show targets that are alive\n");
Expand Down
2 changes: 1 addition & 1 deletion src/fping.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ extern int random_data_flag;
int open_ping_socket_ipv4(int *socktype);
void init_ping_buffer_ipv4(size_t ping_data_size);
void socket_set_src_addr_ipv4(int s, struct in_addr *src_addr, int *ident);
int socket_sendto_ping_ipv4(int s, struct sockaddr *saddr, socklen_t saddr_len, uint16_t icmp_seq, uint16_t icmp_id);
int socket_sendto_ping_ipv4(int s, struct sockaddr *saddr, socklen_t saddr_len, uint16_t icmp_seq, uint16_t icmp_id, uint8_t icmp_proto);
#ifdef IPV6
int open_ping_socket_ipv6(int *socktype);
void init_ping_buffer_ipv6(size_t ping_data_size);
Expand Down
14 changes: 12 additions & 2 deletions src/socket4.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <time.h>

char* ping_buffer_ipv4 = 0;
size_t ping_pkt_size_ipv4;
Expand Down Expand Up @@ -129,14 +130,23 @@ unsigned short calcsum(unsigned short* buffer, int length)
return ~sum;
}

int socket_sendto_ping_ipv4(int s, struct sockaddr* saddr, socklen_t saddr_len, uint16_t icmp_seq_nr, uint16_t icmp_id_nr)
int socket_sendto_ping_ipv4(int s, struct sockaddr* saddr, socklen_t saddr_len, uint16_t icmp_seq_nr, uint16_t icmp_id_nr, uint8_t icmp_proto)
{
struct icmp* icp;
struct timespec tsorig;
long tsorig_ms;
int n;

icp = (struct icmp*)ping_buffer_ipv4;

icp->icmp_type = ICMP_ECHO;
icp->icmp_type = icmp_proto;
if(icmp_proto == ICMP_TSTAMP) {
clock_gettime(CLOCK_REALTIME, &tsorig);
tsorig_ms = (tsorig.tv_sec % (24*60*60)) * 1000 + tsorig.tv_nsec / 1000000;
icp->icmp_otime = htonl(tsorig_ms);
icp->icmp_rtime = 0;
icp->icmp_ttime = 0;
}
icp->icmp_code = 0;
icp->icmp_cksum = 0;
icp->icmp_seq = htons(icmp_seq_nr);
Expand Down
Loading