feat: rework timestamps
This commit is contained in:
parent
825bc1e3c0
commit
259b898324
17 changed files with 245 additions and 240 deletions
12
includes/internal/ping/handlers.h
Normal file
12
includes/internal/ping/handlers.h
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef PING_HANDLERS_H
|
||||||
|
#define PING_HANDLERS_H
|
||||||
|
|
||||||
|
#include "icmp.h"
|
||||||
|
#include "internal/ping/ping_state.h"
|
||||||
|
|
||||||
|
void ping_handle_echo_reply(struct ping_state *state,
|
||||||
|
const icmp_reply_t *reply);
|
||||||
|
void ping_handle_icmp_error(struct ping_state *state,
|
||||||
|
const icmp_reply_t *reply);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -10,7 +10,8 @@
|
||||||
void ping_output_start(const struct ping_config *config,
|
void ping_output_start(const struct ping_config *config,
|
||||||
struct destinations *dest, size_t payload_bytes);
|
struct destinations *dest, size_t payload_bytes);
|
||||||
void ping_output_packet(const icmp_reply_t *reply, uint16_t seq,
|
void ping_output_packet(const icmp_reply_t *reply, uint16_t seq,
|
||||||
int64_t rtt_ns, size_t payload_bytes, const struct ping_config *config);
|
int64_t rtt_ns, size_t payload_bytes, const struct ping_config *config,
|
||||||
|
int is_dup);
|
||||||
void ping_output_error(const icmp_reply_t *reply,
|
void ping_output_error(const icmp_reply_t *reply,
|
||||||
const icmp_offending_packet_t *offending,
|
const icmp_offending_packet_t *offending,
|
||||||
uint16_t seq, const struct ping_config *config);
|
uint16_t seq, const struct ping_config *config);
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ struct ping_state {
|
||||||
volatile sig_atomic_t send_flag;
|
volatile sig_atomic_t send_flag;
|
||||||
volatile sig_atomic_t stop_flag;
|
volatile sig_atomic_t stop_flag;
|
||||||
size_t nb_errors;
|
size_t nb_errors;
|
||||||
|
int ts_in_payload;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -3,28 +3,16 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include "internal/ping/tracker_flags.h"
|
|
||||||
|
|
||||||
#define PING_TRACKER_SLOTS 128
|
|
||||||
|
|
||||||
struct ping_tracker_slot {
|
|
||||||
struct timespec ts;
|
|
||||||
uint8_t flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ping_tracker {
|
struct ping_tracker {
|
||||||
struct ping_tracker_slot slots[PING_TRACKER_SLOTS];
|
uint8_t rcvd[UINT16_MAX / 8];
|
||||||
size_t nb_sent;
|
size_t nb_sent;
|
||||||
size_t nb_recv;
|
size_t nb_recv;
|
||||||
|
size_t nb_dup;
|
||||||
};
|
};
|
||||||
|
|
||||||
void ping_tracker_init(struct ping_tracker *t);
|
void ping_tracker_init(struct ping_tracker *t);
|
||||||
void ping_tracker_record_send(struct ping_tracker *t, uint16_t seq, const
|
void ping_tracker_record_send(struct ping_tracker *t, uint16_t seq);
|
||||||
struct timespec *ts);
|
int ping_tracker_record_recv(struct ping_tracker *t, uint16_t seq);
|
||||||
int64_t ping_tracker_record_recv(struct ping_tracker *t, uint16_t seq,
|
|
||||||
const struct timespec *ts);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef FT_PING_CONST
|
#ifndef FT_PING_CONST
|
||||||
#define FT_PING_CONST
|
#define FT_PING_CONST
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
/* Default configuration values */
|
/* Default configuration values */
|
||||||
#define DEFAULT_COUNT 0
|
#define DEFAULT_COUNT 0
|
||||||
#define DEFAULT_PRELOAD 1
|
#define DEFAULT_PRELOAD 1
|
||||||
|
|
@ -13,4 +15,7 @@
|
||||||
#define MAX_PACKET_SIZE 65507
|
#define MAX_PACKET_SIZE 65507
|
||||||
#define MAX_TTL 255
|
#define MAX_TTL 255
|
||||||
|
|
||||||
|
/* Minimum payload size to embed a send timestamp */
|
||||||
|
#define PING_TIMESTAMP_SIZE ((size_t)sizeof(struct timespec))
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,8 @@ libping_core_la_SOURCES = \
|
||||||
core/loop.c \
|
core/loop.c \
|
||||||
core/send.c \
|
core/send.c \
|
||||||
core/callback.c \
|
core/callback.c \
|
||||||
|
core/reply.c \
|
||||||
|
core/error.c \
|
||||||
tracker/init.c \
|
tracker/init.c \
|
||||||
tracker/record_send.c \
|
tracker/record_send.c \
|
||||||
tracker/record_recv.c \
|
tracker/record_recv.c \
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,5 @@
|
||||||
#include "icmp_types.h"
|
#include "icmp_types.h"
|
||||||
#include "ping/ft_ping_flags.h"
|
#include "internal/ping/handlers.h"
|
||||||
#include "internal/ping/output.h"
|
|
||||||
|
|
||||||
/* Forward declarations */
|
|
||||||
static int extract_our_echo(const icmp_reply_t *reply, uint16_t our_id,
|
|
||||||
struct in_addr dest, uint16_t *seq_out);
|
|
||||||
static void handle_echo_reply(struct ping_state *state,
|
|
||||||
const icmp_reply_t *reply);
|
|
||||||
static int extract_our_error(const icmp_reply_t *reply, uint16_t our_id,
|
|
||||||
struct in_addr dest, icmp_offending_packet_t *out, uint16_t *seq_out);
|
|
||||||
static void handle_icmp_error(struct ping_state *state,
|
|
||||||
const icmp_reply_t *reply);
|
|
||||||
/* -------------------- */
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ping_callback(const icmp_reply_t *reply, void *userdata)
|
ping_callback(const icmp_reply_t *reply, void *userdata)
|
||||||
|
|
@ -20,69 +8,8 @@ ping_callback(const icmp_reply_t *reply, void *userdata)
|
||||||
|
|
||||||
state = (struct ping_state *)userdata;
|
state = (struct ping_state *)userdata;
|
||||||
if (ICMP_TYPE_ECHO_REPLY == reply->type)
|
if (ICMP_TYPE_ECHO_REPLY == reply->type)
|
||||||
handle_echo_reply(state, reply);
|
ping_handle_echo_reply(state, reply);
|
||||||
else if (ICMP_TYPE_TIME_EXCEEDED == reply->type
|
else if (ICMP_TYPE_TIME_EXCEEDED == reply->type
|
||||||
|| ICMP_TYPE_DEST_UNREACHABLE == reply->type)
|
|| ICMP_TYPE_DEST_UNREACHABLE == reply->type)
|
||||||
handle_icmp_error(state, reply);
|
ping_handle_icmp_error(state, reply);
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
extract_our_echo(const icmp_reply_t *reply, uint16_t our_id,
|
|
||||||
struct in_addr dest, uint16_t *seq_out)
|
|
||||||
{
|
|
||||||
uint16_t id;
|
|
||||||
uint16_t seq;
|
|
||||||
|
|
||||||
if (0 > icmp_reply_id_seq(reply, &id, &seq))
|
|
||||||
return 0;
|
|
||||||
if (our_id != id || reply->from.s_addr != dest.s_addr)
|
|
||||||
return 0;
|
|
||||||
*seq_out = seq;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_echo_reply(struct ping_state *state, const icmp_reply_t *reply)
|
|
||||||
{
|
|
||||||
uint16_t seq;
|
|
||||||
int64_t rtt;
|
|
||||||
|
|
||||||
if (0 == extract_our_echo(reply, state->id, state->dest, &seq))
|
|
||||||
return;
|
|
||||||
rtt = ping_tracker_record_recv(state->tracker, seq, &reply->timestamp);
|
|
||||||
if (0 > rtt)
|
|
||||||
return;
|
|
||||||
ping_stats_update(state->stats, rtt);
|
|
||||||
if (HAS_FLAG(state->config->flags, FLAG_FLOOD))
|
|
||||||
ping_output_flood_erase();
|
|
||||||
else
|
|
||||||
ping_output_packet(reply, seq, rtt, state->config->packet_size,
|
|
||||||
state->config);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
extract_our_error(const icmp_reply_t *reply, uint16_t our_id,
|
|
||||||
struct in_addr dest, icmp_offending_packet_t *out, uint16_t *seq_out)
|
|
||||||
{
|
|
||||||
if ((0 > icmp_error_extract_offending(reply, out))
|
|
||||||
|| ICMP_TYPE_ECHO_REQUEST != out->icmp_type
|
|
||||||
|| our_id != out->rest.echo.id || out->dst.s_addr != dest.s_addr)
|
|
||||||
return 0;
|
|
||||||
*seq_out = out->rest.echo.seq;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_icmp_error(struct ping_state *state, const icmp_reply_t *reply)
|
|
||||||
{
|
|
||||||
icmp_offending_packet_t offending;
|
|
||||||
uint16_t seq;
|
|
||||||
|
|
||||||
if (0 == extract_our_error(reply, state->id, state->dest, &offending, &seq))
|
|
||||||
return;
|
|
||||||
if (0 > ping_tracker_record_recv(state->tracker, seq, &reply->timestamp))
|
|
||||||
return;
|
|
||||||
state->nb_errors++;
|
|
||||||
if (!HAS_FLAG(state->config->flags, FLAG_QUIET))
|
|
||||||
ping_output_error(reply, &offending, seq, state->config);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
37
src/ping/core/error.c
Normal file
37
src/ping/core/error.c
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include "icmp.h"
|
||||||
|
#include "icmp_types.h"
|
||||||
|
#include "ping/ft_ping_flags.h"
|
||||||
|
#include "internal/ping/handlers.h"
|
||||||
|
#include "internal/ping/output.h"
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
static int extract_our_error(const icmp_reply_t *reply, uint16_t our_id,
|
||||||
|
struct in_addr dest, icmp_offending_packet_t *out, uint16_t *seq_out);
|
||||||
|
/* -------------------- */
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_handle_icmp_error(struct ping_state *state, const icmp_reply_t *reply)
|
||||||
|
{
|
||||||
|
icmp_offending_packet_t offending;
|
||||||
|
uint16_t seq;
|
||||||
|
|
||||||
|
if (0 == extract_our_error(reply, state->id, state->dest, &offending, &seq))
|
||||||
|
return;
|
||||||
|
if (0 == ping_tracker_record_recv(state->tracker, seq))
|
||||||
|
return;
|
||||||
|
state->nb_errors++;
|
||||||
|
if (!HAS_FLAG(state->config->flags, FLAG_QUIET))
|
||||||
|
ping_output_error(reply, &offending, seq, state->config);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
extract_our_error(const icmp_reply_t *reply, uint16_t our_id,
|
||||||
|
struct in_addr dest, icmp_offending_packet_t *out, uint16_t *seq_out)
|
||||||
|
{
|
||||||
|
if ((0 > icmp_error_extract_offending(reply, out))
|
||||||
|
|| ICMP_TYPE_ECHO_REQUEST != out->icmp_type
|
||||||
|
|| our_id != out->rest.echo.id || out->dst.s_addr != dest.s_addr)
|
||||||
|
return 0;
|
||||||
|
*seq_out = out->rest.echo.seq;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "version_gen.h"
|
#include "version_gen.h"
|
||||||
|
#include "ping/ft_ping_const.h"
|
||||||
#include "ping/ft_ping_flags.h"
|
#include "ping/ft_ping_flags.h"
|
||||||
#include "internal/ping/output.h"
|
#include "internal/ping/output.h"
|
||||||
#include "internal/ping/scheduler.h"
|
#include "internal/ping/scheduler.h"
|
||||||
|
|
@ -108,6 +109,7 @@ ping_init_state(struct ping_state *state, struct ping_stats *stats,
|
||||||
state->tracker = tracker;
|
state->tracker = tracker;
|
||||||
state->stats = stats;
|
state->stats = stats;
|
||||||
state->handle = handle;
|
state->handle = handle;
|
||||||
|
state->ts_in_payload = (config->packet_size >= PING_TIMESTAMP_SIZE);
|
||||||
icmp_get_time(&state->start_time);
|
icmp_get_time(&state->start_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
56
src/ping/core/reply.c
Normal file
56
src/ping/core/reply.c
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "icmp.h"
|
||||||
|
#include "ping/ft_ping_const.h"
|
||||||
|
#include "ping/ft_ping_flags.h"
|
||||||
|
#include "internal/ping/handlers.h"
|
||||||
|
#include "internal/ping/output.h"
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
static int extract_our_echo(const icmp_reply_t *reply, uint16_t our_id,
|
||||||
|
struct in_addr dest, uint16_t *seq_out);
|
||||||
|
/* -------------------- */
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_handle_echo_reply(struct ping_state *state, const icmp_reply_t *reply)
|
||||||
|
{
|
||||||
|
uint16_t seq;
|
||||||
|
int is_new;
|
||||||
|
int64_t rtt_ns;
|
||||||
|
struct timespec send_ts;
|
||||||
|
|
||||||
|
if (0 == extract_our_echo(reply, state->id, state->dest, &seq))
|
||||||
|
return;
|
||||||
|
is_new = ping_tracker_record_recv(state->tracker, seq);
|
||||||
|
rtt_ns = -1;
|
||||||
|
if (state->ts_in_payload && reply->payload_len >= PING_TIMESTAMP_SIZE)
|
||||||
|
{
|
||||||
|
memcpy(&send_ts, reply->payload, sizeof(send_ts));
|
||||||
|
rtt_ns = icmp_time_diff_ns(&send_ts, &reply->timestamp);
|
||||||
|
if (is_new)
|
||||||
|
ping_stats_update(state->stats, rtt_ns);
|
||||||
|
}
|
||||||
|
if (HAS_FLAG(state->config->flags, FLAG_FLOOD))
|
||||||
|
{
|
||||||
|
if (is_new)
|
||||||
|
ping_output_flood_erase();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ping_output_packet(reply, seq, rtt_ns, state->config->packet_size,
|
||||||
|
state->config, !is_new);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
extract_our_echo(const icmp_reply_t *reply, uint16_t our_id,
|
||||||
|
struct in_addr dest, uint16_t *seq_out)
|
||||||
|
{
|
||||||
|
uint16_t id;
|
||||||
|
uint16_t seq;
|
||||||
|
|
||||||
|
if (0 > icmp_reply_id_seq(reply, &id, &seq))
|
||||||
|
return 0;
|
||||||
|
if (our_id != id || reply->from.s_addr != dest.s_addr)
|
||||||
|
return 0;
|
||||||
|
*seq_out = seq;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
@ -12,10 +12,10 @@
|
||||||
#include "ping/ft_ping_flags.h"
|
#include "ping/ft_ping_flags.h"
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
static inline void fill_payload(uint8_t *payload, size_t len, const struct
|
static inline void fill_payload(uint8_t *payload, size_t len,
|
||||||
ping_config *config);
|
const struct ping_config *config, int ts_in_payload);
|
||||||
static int ping_send_one(struct ping_state *state, uint16_t seq,
|
static int ping_send_one(struct ping_state *state, uint16_t seq,
|
||||||
const uint8_t *payload, size_t payload_len, struct timespec *ts);
|
uint8_t *payload, size_t payload_len, struct timespec *ts);
|
||||||
/* -------------------- */
|
/* -------------------- */
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -26,14 +26,14 @@ do_send(struct ping_state *state, size_t payload_len)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
state->send_flag = 0;
|
state->send_flag = 0;
|
||||||
fill_payload(payload, payload_len, state->config);
|
fill_payload(payload, payload_len, state->config, state->ts_in_payload);
|
||||||
ret = ping_send_one(state, state->seq, payload, payload_len, &ts);
|
ret = ping_send_one(state, state->seq, payload, payload_len, &ts);
|
||||||
if (-1 == ret)
|
if (-1 == ret)
|
||||||
dprintf(STDERR_FILENO, "%s: %s\n",
|
dprintf(STDERR_FILENO, "%s: %s\n",
|
||||||
g_prog_name, icmp_strerror(state->handle));
|
g_prog_name, icmp_strerror(state->handle));
|
||||||
else if (0 == ret)
|
else if (0 == ret)
|
||||||
{
|
{
|
||||||
ping_tracker_record_send(state->tracker, state->seq, &ts);
|
ping_tracker_record_send(state->tracker, state->seq);
|
||||||
state->seq++;
|
state->seq++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,12 +49,14 @@ do_send(struct ping_state *state, size_t payload_len)
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ping_send_one(struct ping_state *state, uint16_t seq,
|
ping_send_one(struct ping_state *state, uint16_t seq,
|
||||||
const uint8_t *payload, size_t payload_len, struct timespec *ts)
|
uint8_t *payload, size_t payload_len, struct timespec *ts)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (0 != icmp_get_time(ts))
|
if (0 != icmp_get_time(ts))
|
||||||
return -1;
|
return -1;
|
||||||
|
if (state->ts_in_payload)
|
||||||
|
memcpy(payload, ts, sizeof(*ts));
|
||||||
ret = icmp_send_echo(state->handle,
|
ret = icmp_send_echo(state->handle,
|
||||||
state->dest,
|
state->dest,
|
||||||
state->id,
|
state->id,
|
||||||
|
|
@ -69,16 +71,21 @@ ping_send_one(struct ping_state *state, uint16_t seq,
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
fill_payload(uint8_t *payload, size_t len, const struct ping_config *config)
|
fill_payload(uint8_t *payload, size_t len, const struct ping_config *config,
|
||||||
|
int ts_in_payload)
|
||||||
{
|
{
|
||||||
|
size_t offset;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
offset = ts_in_payload ? PING_TIMESTAMP_SIZE : 0;
|
||||||
if (0 == config->pattern_len)
|
if (0 == config->pattern_len)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < len; ++i)
|
for (i = offset; i < len; ++i)
|
||||||
payload[i] = (uint8_t)('a' + (i % 26));
|
payload[i] = (uint8_t)('a' + ((i - offset) % 26));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < len; ++i)
|
for (i = offset; i < len; ++i)
|
||||||
payload[i] = config->pattern[i % config->pattern_len];
|
payload[i] = config->pattern[(i - offset) % config->pattern_len];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,27 @@
|
||||||
|
|
||||||
void
|
void
|
||||||
ping_output_packet(const icmp_reply_t *reply, uint16_t seq,
|
ping_output_packet(const icmp_reply_t *reply, uint16_t seq,
|
||||||
int64_t rtt_ns, size_t payload_bytes, const struct ping_config *config)
|
int64_t rtt_ns, size_t payload_bytes, const struct ping_config *config,
|
||||||
|
int is_dup)
|
||||||
{
|
{
|
||||||
char from_str[INET_ADDRSTRLEN];
|
char from_str[INET_ADDRSTRLEN];
|
||||||
|
|
||||||
if (HAS_FLAG(config->flags, FLAG_QUIET | FLAG_FLOOD))
|
if (HAS_FLAG(config->flags, FLAG_QUIET | FLAG_FLOOD))
|
||||||
return;
|
return;
|
||||||
inet_ntop(AF_INET, &reply->from, from_str, sizeof(from_str));
|
inet_ntop(AF_INET, &reply->from, from_str, sizeof(from_str));
|
||||||
printf("%zu bytes from %s: icmp_seq=%u ttl=%u time=%.3f ms\n",
|
if (0 <= rtt_ns)
|
||||||
payload_bytes + 8,
|
printf("%zu bytes from %s: icmp_seq=%u ttl=%u time=%.3f ms%s\n",
|
||||||
from_str,
|
payload_bytes + 8,
|
||||||
(unsigned int)seq,
|
from_str,
|
||||||
(unsigned int)reply->ttl,
|
(unsigned int)seq,
|
||||||
(double)rtt_ns / 1e6);
|
(unsigned int)reply->ttl,
|
||||||
|
(double)rtt_ns / 1e6,
|
||||||
|
is_dup ? " (DUP!)" : "");
|
||||||
|
else
|
||||||
|
printf("%zu bytes from %s: icmp_seq=%u ttl=%u%s\n",
|
||||||
|
payload_bytes + 8,
|
||||||
|
from_str,
|
||||||
|
(unsigned int)seq,
|
||||||
|
(unsigned int)reply->ttl,
|
||||||
|
is_dup ? " (DUP!)" : "");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ ping_output_summary(const struct ping_state *state,
|
||||||
ok_recv = state->tracker->nb_recv - errors;
|
ok_recv = state->tracker->nb_recv - errors;
|
||||||
printf("--- %s ping statistics ---\n", dest->dom);
|
printf("--- %s ping statistics ---\n", dest->dom);
|
||||||
print_loss_line(sent, ok_recv, errors);
|
print_loss_line(sent, ok_recv, errors);
|
||||||
if (0 < ok_recv)
|
if (0 < state->stats->count)
|
||||||
print_rtt_line(state->stats);
|
print_rtt_line(state->stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,19 @@
|
||||||
#include "icmp.h"
|
|
||||||
#include "internal/ping/tracker.h"
|
#include "internal/ping/tracker.h"
|
||||||
|
|
||||||
int64_t
|
int
|
||||||
ping_tracker_record_recv(struct ping_tracker *t, uint16_t seq,
|
ping_tracker_record_recv(struct ping_tracker *t, uint16_t seq)
|
||||||
const struct timespec *ts)
|
|
||||||
{
|
{
|
||||||
struct ping_tracker_slot *slot;
|
uint8_t *byte;
|
||||||
|
uint8_t bit;
|
||||||
|
|
||||||
slot = &t->slots[seq % PING_TRACKER_SLOTS];
|
byte = &t->rcvd[seq >> 3];
|
||||||
if ((slot->flags & (TRACKER_SLOT_USED | TRACKER_SLOT_ACKED))
|
bit = (uint8_t)(1u << (seq & 7u)); /* 7 == 0b111 */
|
||||||
!= TRACKER_SLOT_USED)
|
if (*byte & bit)
|
||||||
return (-1);
|
{
|
||||||
slot->flags |= TRACKER_SLOT_ACKED;
|
t->nb_dup++;
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
*byte |= bit;
|
||||||
t->nb_recv++;
|
t->nb_recv++;
|
||||||
return (icmp_time_diff_ns(&slot->ts, ts));
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,8 @@
|
||||||
|
#include "compiler.h"
|
||||||
#include "internal/ping/tracker.h"
|
#include "internal/ping/tracker.h"
|
||||||
|
|
||||||
void
|
void
|
||||||
ping_tracker_record_send(struct ping_tracker *t, uint16_t seq,
|
ping_tracker_record_send(struct ping_tracker *t, uint16_t seq __unused)
|
||||||
const struct timespec *ts)
|
|
||||||
{
|
{
|
||||||
struct ping_tracker_slot *slot;
|
|
||||||
|
|
||||||
slot = &t->slots[seq % PING_TRACKER_SLOTS];
|
|
||||||
slot->ts = *ts;
|
|
||||||
slot->flags = TRACKER_SLOT_USED;
|
|
||||||
t->nb_sent++;
|
t->nb_sent++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,99 +2,65 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "internal/ping/tracker.h"
|
#include "internal/ping/tracker.h"
|
||||||
|
|
||||||
static struct timespec
|
|
||||||
make_ts(long sec, long nsec)
|
|
||||||
{
|
|
||||||
struct timespec ts;
|
|
||||||
|
|
||||||
ts.tv_sec = sec;
|
|
||||||
ts.tv_nsec = nsec;
|
|
||||||
return (ts);
|
|
||||||
}
|
|
||||||
|
|
||||||
Test(ping_tracker, init)
|
Test(ping_tracker, init)
|
||||||
{
|
{
|
||||||
struct ping_tracker t;
|
struct ping_tracker t;
|
||||||
|
|
||||||
ping_tracker_init(&t);
|
ping_tracker_init(&t);
|
||||||
cr_assert_eq(t.nb_sent, (size_t)0);
|
cr_assert_eq(t.nb_sent, (size_t)0);
|
||||||
cr_assert_eq(t.nb_recv, (size_t)0);
|
cr_assert_eq(t.nb_recv, (size_t)0);
|
||||||
|
cr_assert_eq(t.nb_dup, (size_t)0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Test(ping_tracker, basic_send_recv)
|
Test(ping_tracker, basic_send_recv)
|
||||||
{
|
{
|
||||||
struct ping_tracker t;
|
struct ping_tracker t;
|
||||||
struct timespec ts_send;
|
int accepted;
|
||||||
struct timespec ts_recv;
|
|
||||||
int64_t rtt;
|
|
||||||
|
|
||||||
ping_tracker_init(&t);
|
ping_tracker_init(&t);
|
||||||
ts_send = make_ts(1, 0);
|
ping_tracker_record_send(&t, 1);
|
||||||
ts_recv = make_ts(1, 10000000); /* 10 ms later */
|
accepted = ping_tracker_record_recv(&t, 1);
|
||||||
ping_tracker_record_send(&t, 1, &ts_send);
|
cr_assert_eq(accepted, 1);
|
||||||
rtt = ping_tracker_record_recv(&t, 1, &ts_recv);
|
|
||||||
cr_assert_eq(rtt, (int64_t)10000000, "Expected RTT of 10ms in ns");
|
|
||||||
cr_assert_eq(t.nb_sent, (size_t)1);
|
cr_assert_eq(t.nb_sent, (size_t)1);
|
||||||
cr_assert_eq(t.nb_recv, (size_t)1);
|
cr_assert_eq(t.nb_recv, (size_t)1);
|
||||||
|
cr_assert_eq(t.nb_dup, (size_t)0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Test(ping_tracker, double_recv_ignored)
|
Test(ping_tracker, double_recv_is_dup)
|
||||||
{
|
{
|
||||||
struct ping_tracker t;
|
struct ping_tracker t;
|
||||||
struct timespec ts_send;
|
int accepted;
|
||||||
struct timespec ts_recv;
|
|
||||||
int64_t rtt;
|
|
||||||
|
|
||||||
ping_tracker_init(&t);
|
ping_tracker_init(&t);
|
||||||
ts_send = make_ts(0, 0);
|
ping_tracker_record_send(&t, 5);
|
||||||
ts_recv = make_ts(0, 5000000);
|
accepted = ping_tracker_record_recv(&t, 5);
|
||||||
ping_tracker_record_send(&t, 5, &ts_send);
|
cr_assert_eq(accepted, 1, "First recv should be accepted");
|
||||||
rtt = ping_tracker_record_recv(&t, 5, &ts_recv);
|
accepted = ping_tracker_record_recv(&t, 5);
|
||||||
cr_assert(rtt >= 0, "First recv should succeed");
|
cr_assert_eq(accepted, 0, "Duplicate recv should return 0");
|
||||||
rtt = ping_tracker_record_recv(&t, 5, &ts_recv);
|
|
||||||
cr_assert_eq(rtt, (int64_t)-1, "Duplicate recv should return -1");
|
|
||||||
cr_assert_eq(t.nb_recv, (size_t)1);
|
cr_assert_eq(t.nb_recv, (size_t)1);
|
||||||
|
cr_assert_eq(t.nb_dup, (size_t)1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Test(ping_tracker, unknown_seq_returns_minus_one)
|
Test(ping_tracker, unknown_seq_accepted)
|
||||||
{
|
{
|
||||||
struct ping_tracker t;
|
struct ping_tracker t;
|
||||||
struct timespec ts;
|
int accepted;
|
||||||
int64_t rtt;
|
|
||||||
|
|
||||||
ping_tracker_init(&t);
|
ping_tracker_init(&t);
|
||||||
ts = make_ts(0, 0);
|
accepted = ping_tracker_record_recv(&t, 42);
|
||||||
rtt = ping_tracker_record_recv(&t, 42, &ts);
|
cr_assert_eq(accepted, 1);
|
||||||
cr_assert_eq(rtt, (int64_t)-1);
|
cr_assert_eq(t.nb_recv, (size_t)1);
|
||||||
}
|
|
||||||
|
|
||||||
Test(ping_tracker, wraparound_seq)
|
|
||||||
{
|
|
||||||
struct ping_tracker t;
|
|
||||||
struct timespec ts_send;
|
|
||||||
struct timespec ts_recv;
|
|
||||||
int64_t rtt;
|
|
||||||
|
|
||||||
ping_tracker_init(&t);
|
|
||||||
ts_send = make_ts(0, 0);
|
|
||||||
ts_recv = make_ts(0, 1000000);
|
|
||||||
/* seq 128 maps to slot 0 */
|
|
||||||
ping_tracker_record_send(&t, 128, &ts_send);
|
|
||||||
rtt = ping_tracker_record_recv(&t, 128, &ts_recv);
|
|
||||||
cr_assert_eq(rtt, (int64_t)1000000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Test(ping_tracker, lost_count)
|
Test(ping_tracker, lost_count)
|
||||||
{
|
{
|
||||||
struct ping_tracker t;
|
struct ping_tracker t;
|
||||||
struct timespec ts;
|
|
||||||
|
|
||||||
ping_tracker_init(&t);
|
ping_tracker_init(&t);
|
||||||
ts = make_ts(0, 0);
|
ping_tracker_record_send(&t, 1);
|
||||||
ping_tracker_record_send(&t, 1, &ts);
|
ping_tracker_record_send(&t, 2);
|
||||||
ping_tracker_record_send(&t, 2, &ts);
|
ping_tracker_record_send(&t, 3);
|
||||||
ping_tracker_record_send(&t, 3, &ts);
|
ping_tracker_record_recv(&t, 2);
|
||||||
ping_tracker_record_recv(&t, 2, &ts);
|
|
||||||
cr_assert_eq(t.nb_sent, (size_t)3);
|
cr_assert_eq(t.nb_sent, (size_t)3);
|
||||||
cr_assert_eq(t.nb_recv, (size_t)1);
|
cr_assert_eq(t.nb_recv, (size_t)1);
|
||||||
cr_assert_eq(t.nb_sent - t.nb_recv, (size_t)2);
|
cr_assert_eq(t.nb_sent - t.nb_recv, (size_t)2);
|
||||||
|
|
@ -103,63 +69,36 @@ Test(ping_tracker, lost_count)
|
||||||
Test(ping_tracker, sent_count_increments)
|
Test(ping_tracker, sent_count_increments)
|
||||||
{
|
{
|
||||||
struct ping_tracker t;
|
struct ping_tracker t;
|
||||||
struct timespec ts;
|
|
||||||
|
|
||||||
ping_tracker_init(&t);
|
ping_tracker_init(&t);
|
||||||
ts = make_ts(0, 0);
|
ping_tracker_record_send(&t, 1);
|
||||||
ping_tracker_record_send(&t, 1, &ts);
|
ping_tracker_record_send(&t, 2);
|
||||||
ping_tracker_record_send(&t, 2, &ts);
|
ping_tracker_record_send(&t, 3);
|
||||||
ping_tracker_record_send(&t, 3, &ts);
|
|
||||||
cr_assert_eq(t.nb_sent, (size_t)3);
|
cr_assert_eq(t.nb_sent, (size_t)3);
|
||||||
}
|
}
|
||||||
|
|
||||||
Test(ping_tracker, recv_rtt_positive)
|
Test(ping_tracker, no_slot_collision_at_256)
|
||||||
{
|
{
|
||||||
struct ping_tracker t;
|
struct ping_tracker t;
|
||||||
struct timespec ts_send;
|
|
||||||
struct timespec ts_recv;
|
|
||||||
int64_t rtt;
|
|
||||||
|
|
||||||
ping_tracker_init(&t);
|
|
||||||
ts_send = make_ts(1, 0);
|
|
||||||
ts_recv = make_ts(1, 10000000); /* 10 ms later */
|
|
||||||
ping_tracker_record_send(&t, 7, &ts_send);
|
|
||||||
rtt = ping_tracker_record_recv(&t, 7, &ts_recv);
|
|
||||||
cr_assert(rtt > 0, "RTT should be positive");
|
|
||||||
}
|
|
||||||
|
|
||||||
Test(ping_tracker, slot_reuse_after_wraparound)
|
|
||||||
{
|
|
||||||
struct ping_tracker t;
|
|
||||||
struct timespec ts_send;
|
|
||||||
struct timespec ts_recv;
|
|
||||||
int64_t rtt;
|
|
||||||
|
|
||||||
ping_tracker_init(&t);
|
|
||||||
ts_send = make_ts(0, 0);
|
|
||||||
ts_recv = make_ts(0, 1000000);
|
|
||||||
/* seq=1 -> slot 1 */
|
|
||||||
ping_tracker_record_send(&t, 1, &ts_send);
|
|
||||||
rtt = ping_tracker_record_recv(&t, 1, &ts_recv);
|
|
||||||
cr_assert(rtt >= 0, "First round RTT should be >= 0");
|
|
||||||
/* seq=129 -> slot 129 % 128 = 1, same slot */
|
|
||||||
ping_tracker_record_send(&t, 129, &ts_send);
|
|
||||||
rtt = ping_tracker_record_recv(&t, 129, &ts_recv);
|
|
||||||
cr_assert(rtt >= 0, "Second round RTT should be >= 0");
|
|
||||||
cr_assert_eq(t.nb_sent, (size_t)2);
|
|
||||||
cr_assert_eq(t.nb_recv, (size_t)2);
|
|
||||||
}
|
|
||||||
|
|
||||||
Test(ping_tracker, full_buffer)
|
|
||||||
{
|
|
||||||
struct ping_tracker t;
|
|
||||||
struct timespec ts;
|
|
||||||
uint16_t seq;
|
uint16_t seq;
|
||||||
|
|
||||||
ping_tracker_init(&t);
|
ping_tracker_init(&t);
|
||||||
ts = make_ts(0, 0);
|
for (seq = 0; seq < 256; seq++)
|
||||||
for (seq = 1; seq <= 128; seq++)
|
{
|
||||||
ping_tracker_record_send(&t, seq, &ts);
|
ping_tracker_record_send(&t, seq);
|
||||||
cr_assert_eq(t.nb_sent, (size_t)128);
|
cr_assert_eq(ping_tracker_record_recv(&t, seq), 1,
|
||||||
cr_assert_eq(t.nb_recv, (size_t)0);
|
"seq %u should be accepted", (unsigned)seq);
|
||||||
|
}
|
||||||
|
cr_assert_eq(t.nb_recv, (size_t)256);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(ping_tracker, full_seq_space)
|
||||||
|
{
|
||||||
|
struct ping_tracker t;
|
||||||
|
uint32_t seq;
|
||||||
|
|
||||||
|
ping_tracker_init(&t);
|
||||||
|
for (seq = 0; seq < 65536; seq++)
|
||||||
|
ping_tracker_record_send(&t, (uint16_t)seq);
|
||||||
|
cr_assert_eq(t.nb_sent, (size_t)65536);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include "internal/ping/ping_state.h"
|
#include "internal/ping/ping_state.h"
|
||||||
#include "internal/ping/tracker.h"
|
#include "internal/ping/tracker.h"
|
||||||
#include "ping/ping.h"
|
#include "ping/ping.h"
|
||||||
|
#include "ping/ft_ping_const.h"
|
||||||
|
|
||||||
/* Controllable mock state */
|
/* Controllable mock state */
|
||||||
int g_mock_send_ret;
|
int g_mock_send_ret;
|
||||||
|
|
@ -104,3 +105,23 @@ Test(do_send, eagain_then_success_uses_same_seq, .init = setup)
|
||||||
cr_assert_eq(g_state.seq, (uint16_t)1);
|
cr_assert_eq(g_state.seq, (uint16_t)1);
|
||||||
cr_assert_eq(g_tracker.nb_sent, (size_t)1);
|
cr_assert_eq(g_tracker.nb_sent, (size_t)1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Test(do_send, ts_in_payload_increments_seq, .init = setup)
|
||||||
|
{
|
||||||
|
g_mock_send_ret = 0;
|
||||||
|
g_state.ts_in_payload = 1;
|
||||||
|
do_send(&g_state, PING_TIMESTAMP_SIZE);
|
||||||
|
cr_assert_eq(g_state.seq, (uint16_t)1);
|
||||||
|
cr_assert_eq(g_tracker.nb_sent, (size_t)1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(do_send, ts_in_payload_multiple_sends, .init = setup)
|
||||||
|
{
|
||||||
|
g_mock_send_ret = 0;
|
||||||
|
g_state.ts_in_payload = 1;
|
||||||
|
do_send(&g_state, PING_TIMESTAMP_SIZE);
|
||||||
|
do_send(&g_state, PING_TIMESTAMP_SIZE);
|
||||||
|
do_send(&g_state, PING_TIMESTAMP_SIZE);
|
||||||
|
cr_assert_eq(g_state.seq, (uint16_t)3);
|
||||||
|
cr_assert_eq(g_tracker.nb_sent, (size_t)3);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue