feat: implement ping core
This commit is contained in:
parent
a4ed2780f2
commit
04e1f3f15b
27 changed files with 996 additions and 3 deletions
8
includes/internal/callback.h
Normal file
8
includes/internal/callback.h
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef PING_CALLBACK_H
|
||||||
|
#define PING_CALLBACK_H
|
||||||
|
|
||||||
|
#include "icmp.h"
|
||||||
|
|
||||||
|
void ping_callback(const icmp_reply_t *reply, void *userdata);
|
||||||
|
|
||||||
|
#endif
|
||||||
9
includes/internal/loop.h
Normal file
9
includes/internal/loop.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef PING_LOOP_H
|
||||||
|
#define PING_LOOP_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "internal/ping_state.h"
|
||||||
|
|
||||||
|
void ping_loop(t_ping_state *state, size_t payload_len);
|
||||||
|
|
||||||
|
#endif
|
||||||
21
includes/internal/output.h
Normal file
21
includes/internal/output.h
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef PING_OUTPUT_H
|
||||||
|
#define PING_OUTPUT_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include "icmp.h"
|
||||||
|
#include "ft_ping.h"
|
||||||
|
#include "internal/ping_state.h"
|
||||||
|
|
||||||
|
void ping_output_start(const t_ping_config *config,
|
||||||
|
struct in_addr dest, size_t payload_bytes);
|
||||||
|
void ping_output_packet(const icmp_reply_t *reply, uint16_t seq,
|
||||||
|
int64_t rtt_ns, size_t payload_bytes, const t_ping_config *config);
|
||||||
|
void ping_output_error(const icmp_reply_t *reply,
|
||||||
|
const icmp_offending_packet_t *offending,
|
||||||
|
uint16_t seq, const t_ping_config *config);
|
||||||
|
void ping_output_summary(const t_ping_state *state, struct in_addr dest);
|
||||||
|
void ping_output_flood_dot(void);
|
||||||
|
void ping_output_flood_erase(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
28
includes/internal/ping_state.h
Normal file
28
includes/internal/ping_state.h
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef PING_STATE_H
|
||||||
|
#define PING_STATE_H
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "icmp.h"
|
||||||
|
#include "ft_ping.h"
|
||||||
|
#include "internal/stats.h"
|
||||||
|
#include "internal/tracker.h"
|
||||||
|
|
||||||
|
typedef struct s_ping_state {
|
||||||
|
icmp_handle_t *handle;
|
||||||
|
const t_ping_config *config;
|
||||||
|
struct in_addr dest;
|
||||||
|
struct timespec start_time;
|
||||||
|
struct timespec linger_start;
|
||||||
|
uint16_t id;
|
||||||
|
uint16_t seq;
|
||||||
|
t_ping_tracker *tracker;
|
||||||
|
t_ping_stats *stats;
|
||||||
|
volatile sig_atomic_t send_flag;
|
||||||
|
volatile sig_atomic_t stop_flag;
|
||||||
|
size_t nb_errors;
|
||||||
|
} t_ping_state;
|
||||||
|
|
||||||
|
#endif
|
||||||
12
includes/internal/scheduler.h
Normal file
12
includes/internal/scheduler.h
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef PING_SCHEDULER_H
|
||||||
|
#define PING_SCHEDULER_H
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include "internal/ping_state.h"
|
||||||
|
|
||||||
|
void ping_scheduler_init(t_ping_state *state);
|
||||||
|
void ping_scheduler_arm(const t_ping_config *config);
|
||||||
|
void ping_scheduler_select_tv(const t_ping_config *config,
|
||||||
|
struct timeval *tv);
|
||||||
|
|
||||||
|
#endif
|
||||||
9
includes/internal/send.h
Normal file
9
includes/internal/send.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef PING_SEND_H
|
||||||
|
#define PING_SEND_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "internal/ping_state.h"
|
||||||
|
|
||||||
|
int ping_send_one(t_ping_state *state, size_t payload_len);
|
||||||
|
|
||||||
|
#endif
|
||||||
20
includes/internal/stats.h
Normal file
20
includes/internal/stats.h
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef PING_STATS_H
|
||||||
|
#define PING_STATS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef struct s_ping_stats {
|
||||||
|
int64_t min_ns;
|
||||||
|
int64_t max_ns;
|
||||||
|
int64_t sum_ns;
|
||||||
|
int64_t sum_sq_ns; /* sum of (rtt_ns / 1000)^2 in us^2, to avoid overflow */
|
||||||
|
size_t count;
|
||||||
|
} t_ping_stats;
|
||||||
|
|
||||||
|
void ping_stats_init(t_ping_stats *s);
|
||||||
|
void ping_stats_update(t_ping_stats *s, int64_t rtt_ns);
|
||||||
|
void ping_stats_get(const t_ping_stats *s,
|
||||||
|
double *min_ms, double *max_ms, double *avg_ms, double *mdev_ms);
|
||||||
|
|
||||||
|
#endif
|
||||||
29
includes/internal/tracker.h
Normal file
29
includes/internal/tracker.h
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef PING_TRACKER_H
|
||||||
|
#define PING_TRACKER_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#define PING_TRACKER_SLOTS 128
|
||||||
|
|
||||||
|
typedef struct s_ping_tracker_slot {
|
||||||
|
struct timespec ts;
|
||||||
|
bool used;
|
||||||
|
bool acked;
|
||||||
|
} t_ping_tracker_slot;
|
||||||
|
|
||||||
|
typedef struct s_ping_tracker {
|
||||||
|
t_ping_tracker_slot slots[PING_TRACKER_SLOTS];
|
||||||
|
size_t nb_sent;
|
||||||
|
size_t nb_recv;
|
||||||
|
} t_ping_tracker;
|
||||||
|
|
||||||
|
void ping_tracker_init(t_ping_tracker *t);
|
||||||
|
void ping_tracker_record_send(t_ping_tracker *t, uint16_t seq, const
|
||||||
|
struct timespec *ts);
|
||||||
|
int64_t ping_tracker_record_recv(t_ping_tracker *t, uint16_t seq,
|
||||||
|
const struct timespec *ts);
|
||||||
|
|
||||||
|
#endif
|
||||||
8
includes/ping.h
Normal file
8
includes/ping.h
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef PING_H
|
||||||
|
#define PING_H
|
||||||
|
|
||||||
|
#include "ft_ping.h"
|
||||||
|
|
||||||
|
int ping_run(const t_ping_config *config);
|
||||||
|
|
||||||
|
#endif
|
||||||
17
sources.mk
17
sources.mk
|
|
@ -3,6 +3,7 @@ SRC_DIR = src
|
||||||
SRCS = $(SRC_DIR)/main.c \
|
SRCS = $(SRC_DIR)/main.c \
|
||||||
$(SRC_DIR)/cli/parse.c \
|
$(SRC_DIR)/cli/parse.c \
|
||||||
$(SRC_DIR)/cli/handlers/handle_count.c \
|
$(SRC_DIR)/cli/handlers/handle_count.c \
|
||||||
|
$(SRC_DIR)/cli/handlers/handle_deadline.c \
|
||||||
$(SRC_DIR)/cli/handlers/handle_flood.c \
|
$(SRC_DIR)/cli/handlers/handle_flood.c \
|
||||||
$(SRC_DIR)/cli/handlers/handle_help.c \
|
$(SRC_DIR)/cli/handlers/handle_help.c \
|
||||||
$(SRC_DIR)/cli/handlers/handle_interval.c \
|
$(SRC_DIR)/cli/handlers/handle_interval.c \
|
||||||
|
|
@ -22,6 +23,20 @@ SRCS = $(SRC_DIR)/main.c \
|
||||||
$(SRC_DIR)/cli/messages/help.c \
|
$(SRC_DIR)/cli/messages/help.c \
|
||||||
$(SRC_DIR)/cli/messages/version.c \
|
$(SRC_DIR)/cli/messages/version.c \
|
||||||
$(SRC_DIR)/cli/messages/error.c \
|
$(SRC_DIR)/cli/messages/error.c \
|
||||||
|
$(SRC_DIR)/core/ping.c \
|
||||||
|
$(SRC_DIR)/core/loop.c \
|
||||||
|
$(SRC_DIR)/core/send.c \
|
||||||
|
$(SRC_DIR)/core/callback.c \
|
||||||
|
$(SRC_DIR)/stats/stats.c \
|
||||||
|
$(SRC_DIR)/tracker/init.c \
|
||||||
|
$(SRC_DIR)/tracker/record_send.c \
|
||||||
|
$(SRC_DIR)/tracker/record_recv.c \
|
||||||
|
$(SRC_DIR)/output/start.c \
|
||||||
|
$(SRC_DIR)/output/packet.c \
|
||||||
|
$(SRC_DIR)/output/error.c \
|
||||||
|
$(SRC_DIR)/output/summary.c \
|
||||||
|
$(SRC_DIR)/output/flood.c \
|
||||||
|
$(SRC_DIR)/scheduler/scheduler.c \
|
||||||
|
|
||||||
TESTS_DIR = tests
|
TESTS_DIR = tests
|
||||||
TESTS = $(TESTS_DIR)/test_main.c \
|
TESTS = $(TESTS_DIR)/test_main.c \
|
||||||
|
|
@ -38,4 +53,6 @@ TESTS = $(TESTS_DIR)/test_main.c \
|
||||||
$(TESTS_DIR)/cli/handlers/test_handle_verbose.c \
|
$(TESTS_DIR)/cli/handlers/test_handle_verbose.c \
|
||||||
$(TESTS_DIR)/cli/parse_utils/test_parse_int.c \
|
$(TESTS_DIR)/cli/parse_utils/test_parse_int.c \
|
||||||
$(TESTS_DIR)/cli/parse_utils/test_parse_float.c \
|
$(TESTS_DIR)/cli/parse_utils/test_parse_float.c \
|
||||||
|
$(TESTS_DIR)/ping/stats/test_stats.c \
|
||||||
|
$(TESTS_DIR)/ping/tracker/test_tracker.c \
|
||||||
|
|
||||||
|
|
|
||||||
93
src/core/callback.c
Normal file
93
src/core/callback.c
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
#include "icmp.h"
|
||||||
|
#include "icmp_types.h"
|
||||||
|
#include "ft_ping_flags.h"
|
||||||
|
#include "internal/callback.h"
|
||||||
|
#include "internal/ping_state.h"
|
||||||
|
#include "internal/stats.h"
|
||||||
|
#include "internal/tracker.h"
|
||||||
|
#include "internal/output.h"
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
static int extract_our_echo(const icmp_reply_t *reply, uint16_t our_id,
|
||||||
|
uint16_t *seq_out);
|
||||||
|
static void handle_echo_reply(t_ping_state *state, const icmp_reply_t *reply);
|
||||||
|
static int extract_our_error(const icmp_reply_t *reply, uint16_t our_id,
|
||||||
|
icmp_offending_packet_t *out, uint16_t *seq_out);
|
||||||
|
static void handle_icmp_error(t_ping_state *state, const icmp_reply_t *reply);
|
||||||
|
/* -------------------- */
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_callback(const icmp_reply_t *reply, void *userdata)
|
||||||
|
{
|
||||||
|
t_ping_state *state;
|
||||||
|
|
||||||
|
state = (t_ping_state *)userdata;
|
||||||
|
if (ICMP_TYPE_ECHO_REPLY == reply->type)
|
||||||
|
handle_echo_reply(state, reply);
|
||||||
|
else if (ICMP_TYPE_TIME_EXCEEDED == reply->type
|
||||||
|
|| ICMP_TYPE_DEST_UNREACHABLE == reply->type)
|
||||||
|
handle_icmp_error(state, reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
extract_our_echo(const icmp_reply_t *reply, uint16_t our_id,
|
||||||
|
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)
|
||||||
|
return 0;
|
||||||
|
*seq_out = seq;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_echo_reply(t_ping_state *state, const icmp_reply_t *reply)
|
||||||
|
{
|
||||||
|
uint16_t seq;
|
||||||
|
int64_t rtt;
|
||||||
|
|
||||||
|
if (0 == extract_our_echo(reply, state->id, &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,
|
||||||
|
icmp_offending_packet_t *out, uint16_t *seq_out)
|
||||||
|
{
|
||||||
|
if (0 > icmp_error_extract_offending(reply, out))
|
||||||
|
return 0;
|
||||||
|
if (ICMP_TYPE_ECHO_REQUEST != out->icmp_type)
|
||||||
|
return 0;
|
||||||
|
if (our_id != out->rest.echo.id)
|
||||||
|
return 0;
|
||||||
|
*seq_out = out->rest.echo.seq;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_icmp_error(t_ping_state *state, const icmp_reply_t *reply)
|
||||||
|
{
|
||||||
|
icmp_offending_packet_t offending;
|
||||||
|
uint16_t seq;
|
||||||
|
|
||||||
|
if (0 == extract_our_error(reply, state->id, &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);
|
||||||
|
}
|
||||||
128
src/core/loop.c
Normal file
128
src/core/loop.c
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
#include <sys/select.h>
|
||||||
|
|
||||||
|
#include "icmp.h"
|
||||||
|
#include "ft_ping_flags.h"
|
||||||
|
#include "internal/loop.h"
|
||||||
|
#include "internal/send.h"
|
||||||
|
#include "internal/callback.h"
|
||||||
|
#include "internal/tracker.h"
|
||||||
|
#include "internal/output.h"
|
||||||
|
#include "internal/scheduler.h"
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
static int deadline_expired(const t_ping_state *state);
|
||||||
|
static int linger_expired(const t_ping_state *state);
|
||||||
|
static int should_stop(const t_ping_state *state);
|
||||||
|
static int can_send_more(const t_ping_state *state);
|
||||||
|
static void do_send(t_ping_state *state, size_t payload_len);
|
||||||
|
static void try_recv(t_ping_state *state);
|
||||||
|
static void handle_send_trigger(t_ping_state *state, size_t payload_len);
|
||||||
|
/* -------------------- */
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_loop(t_ping_state *state, size_t payload_len)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
handle_send_trigger(state, payload_len);
|
||||||
|
if (should_stop(state))
|
||||||
|
break;
|
||||||
|
try_recv(state);
|
||||||
|
} while (state->send_flag || !should_stop(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
deadline_expired(const t_ping_state *state)
|
||||||
|
{
|
||||||
|
struct timespec now;
|
||||||
|
double elapsed;
|
||||||
|
|
||||||
|
if (0.0 == state->config->deadline)
|
||||||
|
return 0;
|
||||||
|
icmp_get_time(&now);
|
||||||
|
elapsed = (double)(now.tv_sec - state->start_time.tv_sec)
|
||||||
|
+ (double)(now.tv_nsec - state->start_time.tv_nsec) / 1e9;
|
||||||
|
return elapsed >= state->config->deadline;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
linger_expired(const t_ping_state *state)
|
||||||
|
{
|
||||||
|
struct timespec now;
|
||||||
|
double elapsed;
|
||||||
|
|
||||||
|
if (0 == state->linger_start.tv_sec && 0 == state->linger_start.tv_nsec)
|
||||||
|
return 0;
|
||||||
|
icmp_get_time(&now);
|
||||||
|
elapsed = (double)(now.tv_sec - state->linger_start.tv_sec)
|
||||||
|
+ (double)(now.tv_nsec - state->linger_start.tv_nsec) / 1e9;
|
||||||
|
return elapsed >= state->config->timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
should_stop(const t_ping_state *state)
|
||||||
|
{
|
||||||
|
size_t count;
|
||||||
|
|
||||||
|
if (state->stop_flag)
|
||||||
|
return 1;
|
||||||
|
if (deadline_expired(state))
|
||||||
|
return 1;
|
||||||
|
if (linger_expired(state))
|
||||||
|
return 1;
|
||||||
|
count = state->config->count;
|
||||||
|
if (0 != count && state->tracker->nb_recv >= count)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
can_send_more(const t_ping_state *state)
|
||||||
|
{
|
||||||
|
size_t count;
|
||||||
|
|
||||||
|
count = state->config->count;
|
||||||
|
return 0 == count || state->tracker->nb_sent < count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
do_send(t_ping_state *state, size_t payload_len)
|
||||||
|
{
|
||||||
|
state->send_flag = 0;
|
||||||
|
ping_send_one(state, payload_len);
|
||||||
|
if (HAS_FLAG(state->config->flags, FLAG_FLOOD))
|
||||||
|
ping_output_flood_dot();
|
||||||
|
ping_scheduler_arm(state->config);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
try_recv(t_ping_state *state)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
fd_set rfds;
|
||||||
|
struct timeval tv;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
fd = icmp_get_fd(state->handle);
|
||||||
|
ping_scheduler_select_tv(state->config, &tv);
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(fd, &rfds);
|
||||||
|
ret = select(fd + 1, &rfds, NULL, NULL, &tv);
|
||||||
|
if (0 < ret)
|
||||||
|
icmp_process(state->handle, ping_callback, state, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_send_trigger(t_ping_state *state, size_t payload_len)
|
||||||
|
{
|
||||||
|
if (!state->send_flag && !HAS_FLAG(state->config->flags, FLAG_FLOOD))
|
||||||
|
return;
|
||||||
|
if (can_send_more(state))
|
||||||
|
do_send(state, payload_len);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (0 == state->linger_start.tv_sec && 0 == state->linger_start.tv_nsec)
|
||||||
|
icmp_get_time(&state->linger_start);
|
||||||
|
state->send_flag = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
91
src/core/ping.c
Normal file
91
src/core/ping.c
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "ping.h"
|
||||||
|
#include "icmp.h"
|
||||||
|
#include "ft_ping_flags.h"
|
||||||
|
#include "internal/ping_state.h"
|
||||||
|
#include "internal/stats.h"
|
||||||
|
#include "internal/tracker.h"
|
||||||
|
#include "internal/output.h"
|
||||||
|
#include "internal/scheduler.h"
|
||||||
|
#include "internal/send.h"
|
||||||
|
#include "internal/loop.h"
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
static int ping_one(const t_ping_config *config, struct in_addr dest,
|
||||||
|
icmp_handle_t *handle);
|
||||||
|
static void ping_init_state(t_ping_state *state, t_ping_stats *stats,
|
||||||
|
t_ping_tracker *tracker, const t_ping_config *config, struct in_addr
|
||||||
|
dest, icmp_handle_t *handle);
|
||||||
|
static void ping_first_send(t_ping_state *state, size_t payload_len);
|
||||||
|
/* -------------------- */
|
||||||
|
|
||||||
|
int
|
||||||
|
ping_run(const t_ping_config *config)
|
||||||
|
{
|
||||||
|
icmp_handle_t *handle;
|
||||||
|
int ret;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (0 == config->nb_destinations)
|
||||||
|
return 1;
|
||||||
|
handle = icmp_create();
|
||||||
|
if (NULL == handle)
|
||||||
|
return 1;
|
||||||
|
ret = 0;
|
||||||
|
i = 0;
|
||||||
|
while (i < config->nb_destinations)
|
||||||
|
{
|
||||||
|
if (0 != ping_one(config, config->destinations[i], handle))
|
||||||
|
ret = 1;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
icmp_destroy(handle);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ping_one(const t_ping_config *config, struct in_addr dest,
|
||||||
|
icmp_handle_t *handle)
|
||||||
|
{
|
||||||
|
t_ping_state state;
|
||||||
|
t_ping_stats stats;
|
||||||
|
t_ping_tracker tracker;
|
||||||
|
size_t payload_len;
|
||||||
|
|
||||||
|
ping_init_state(&state, &stats, &tracker, config, dest, handle);
|
||||||
|
payload_len = config->packet_size;
|
||||||
|
ping_scheduler_init(&state);
|
||||||
|
ping_output_start(config, dest, payload_len);
|
||||||
|
ping_first_send(&state, payload_len);
|
||||||
|
ping_loop(&state, payload_len);
|
||||||
|
ping_output_summary(&state, dest);
|
||||||
|
return tracker.nb_recv <= state.nb_errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ping_init_state(t_ping_state *state, t_ping_stats *stats,
|
||||||
|
t_ping_tracker *tracker, const t_ping_config *config,
|
||||||
|
struct in_addr dest, icmp_handle_t *handle)
|
||||||
|
{
|
||||||
|
ping_stats_init(stats);
|
||||||
|
ping_tracker_init(tracker);
|
||||||
|
memset(state, 0, sizeof(*state));
|
||||||
|
state->config = config;
|
||||||
|
state->dest = dest;
|
||||||
|
state->id = (uint16_t)((unsigned int)getpid() & 0xFFFFU);
|
||||||
|
state->tracker = tracker;
|
||||||
|
state->stats = stats;
|
||||||
|
state->handle = handle;
|
||||||
|
icmp_get_time(&state->start_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ping_first_send(t_ping_state *state, size_t payload_len)
|
||||||
|
{
|
||||||
|
ping_send_one(state, payload_len);
|
||||||
|
if (HAS_FLAG(state->config->flags, FLAG_FLOOD))
|
||||||
|
ping_output_flood_dot();
|
||||||
|
ping_scheduler_arm(state->config);
|
||||||
|
}
|
||||||
23
src/core/send.c
Normal file
23
src/core/send.c
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "icmp.h"
|
||||||
|
#include "ft_ping_const.h"
|
||||||
|
#include "internal/send.h"
|
||||||
|
#include "internal/tracker.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
ping_send_one(t_ping_state *state, size_t payload_len)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
uint8_t payload[MAX_PACKET_SIZE];
|
||||||
|
|
||||||
|
memset(payload, 0x42, payload_len);
|
||||||
|
icmp_get_time(&ts);
|
||||||
|
ping_tracker_record_send(state->tracker, state->seq, &ts);
|
||||||
|
return (icmp_send_echo(state->handle,
|
||||||
|
state->dest,
|
||||||
|
state->id,
|
||||||
|
state->seq++,
|
||||||
|
state->config->ttl,
|
||||||
|
payload, payload_len));
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "cli.h"
|
#include "cli.h"
|
||||||
|
#include "ping.h"
|
||||||
#include "version_gen.h"
|
#include "version_gen.h"
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
|
|
@ -15,7 +15,7 @@ int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
t_ping_config config;
|
t_ping_config config;
|
||||||
enum e_cli_code ret;
|
int ret;
|
||||||
|
|
||||||
if (0 != init_prog_name(argv[0]))
|
if (0 != init_prog_name(argv[0]))
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
|
@ -23,7 +23,7 @@ main(int argc, char **argv)
|
||||||
if (ret != CLI_SUCCESS)
|
if (ret != CLI_SUCCESS)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
printf("PING: Not yet implemented\n");
|
ret = ping_run(&config);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
cli_config_free(&config);
|
cli_config_free(&config);
|
||||||
|
|
|
||||||
34
src/output/error.c
Normal file
34
src/output/error.c
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "icmp.h"
|
||||||
|
#include "icmp_types.h"
|
||||||
|
#include "internal/output.h"
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
static const char *error_msg_for(const icmp_reply_t *reply);
|
||||||
|
/* -------------------- */
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_output_error(const icmp_reply_t *reply,
|
||||||
|
const icmp_offending_packet_t *offending,
|
||||||
|
uint16_t seq, const t_ping_config *config)
|
||||||
|
{
|
||||||
|
char from_str[INET_ADDRSTRLEN];
|
||||||
|
|
||||||
|
(void)offending;
|
||||||
|
(void)config;
|
||||||
|
inet_ntop(AF_INET, &reply->from, from_str, sizeof(from_str));
|
||||||
|
fprintf(stderr, "From %s: icmp_seq=%u %s\n",
|
||||||
|
from_str, (unsigned int)seq, error_msg_for(reply));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
error_msg_for(const icmp_reply_t *reply)
|
||||||
|
{
|
||||||
|
if (ICMP_TYPE_TIME_EXCEEDED == reply->type)
|
||||||
|
return ("Time to live exceeded");
|
||||||
|
if (ICMP_CODE_HOST_UNREACHABLE == reply->code)
|
||||||
|
return ("Destination Host Unreachable");
|
||||||
|
return ("Destination Net Unreachable");
|
||||||
|
}
|
||||||
15
src/output/flood.c
Normal file
15
src/output/flood.c
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "internal/output.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_output_flood_dot(void)
|
||||||
|
{
|
||||||
|
write(1, ".", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_output_flood_erase(void)
|
||||||
|
{
|
||||||
|
write(1, "\b \b", 3);
|
||||||
|
}
|
||||||
22
src/output/packet.c
Normal file
22
src/output/packet.c
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "ft_ping_flags.h"
|
||||||
|
#include "internal/output.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_output_packet(const icmp_reply_t *reply, uint16_t seq,
|
||||||
|
int64_t rtt_ns, size_t payload_bytes, const t_ping_config *config)
|
||||||
|
{
|
||||||
|
char from_str[INET_ADDRSTRLEN];
|
||||||
|
|
||||||
|
if (HAS_FLAG(config->flags, FLAG_QUIET) || HAS_FLAG(config->flags, FLAG_FLOOD))
|
||||||
|
return;
|
||||||
|
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",
|
||||||
|
payload_bytes + 8,
|
||||||
|
from_str,
|
||||||
|
(unsigned int)seq,
|
||||||
|
(unsigned int)reply->ttl,
|
||||||
|
(double)rtt_ns / 1e6);
|
||||||
|
}
|
||||||
24
src/output/start.c
Normal file
24
src/output/start.c
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "ft_ping_flags.h"
|
||||||
|
#include "internal/output.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_output_start(const t_ping_config *config,
|
||||||
|
struct in_addr dest, size_t payload_bytes)
|
||||||
|
{
|
||||||
|
char dest_str[INET_ADDRSTRLEN];
|
||||||
|
|
||||||
|
inet_ntop(AF_INET, &dest, dest_str, sizeof(dest_str));
|
||||||
|
printf("PING %s (%s): %zu data bytes", dest_str, dest_str, payload_bytes);
|
||||||
|
if (HAS_FLAG(config->flags, FLAG_VERBOSE))
|
||||||
|
{
|
||||||
|
unsigned int id;
|
||||||
|
|
||||||
|
id = (unsigned int)getpid() & 0xFFFFU;
|
||||||
|
printf(", id 0x%04x = %u", id, id);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
56
src/output/summary.c
Normal file
56
src/output/summary.c
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "internal/output.h"
|
||||||
|
#include "internal/stats.h"
|
||||||
|
#include "internal/tracker.h"
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
static void print_loss_line(size_t sent, size_t ok_recv, size_t errors);
|
||||||
|
static void print_rtt_line(const t_ping_stats *stats);
|
||||||
|
/* -------------------- */
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_output_summary(const t_ping_state *state, struct in_addr dest)
|
||||||
|
{
|
||||||
|
char dest_str[INET_ADDRSTRLEN];
|
||||||
|
size_t sent;
|
||||||
|
size_t errors;
|
||||||
|
size_t ok_recv;
|
||||||
|
|
||||||
|
inet_ntop(AF_INET, &dest, dest_str, sizeof(dest_str));
|
||||||
|
sent = state->tracker->nb_sent;
|
||||||
|
errors = state->nb_errors;
|
||||||
|
ok_recv = state->tracker->nb_recv - errors;
|
||||||
|
printf("--- %s ping statistics ---\n", dest_str);
|
||||||
|
print_loss_line(sent, ok_recv, errors);
|
||||||
|
if (0 < ok_recv)
|
||||||
|
print_rtt_line(state->stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_loss_line(size_t sent, size_t ok_recv, size_t errors)
|
||||||
|
{
|
||||||
|
int loss_pct;
|
||||||
|
|
||||||
|
loss_pct = (0 < sent) ? (int)((sent - ok_recv) * 100 / sent) : 0;
|
||||||
|
if (0 < errors)
|
||||||
|
printf("%zu packets transmitted, %zu received, +%zu errors,"
|
||||||
|
" %d%% packet loss\n", sent, ok_recv, errors, loss_pct);
|
||||||
|
else
|
||||||
|
printf("%zu packets transmitted, %zu received, %d%% packet loss\n",
|
||||||
|
sent, ok_recv, loss_pct);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_rtt_line(const t_ping_stats *stats)
|
||||||
|
{
|
||||||
|
double min_ms;
|
||||||
|
double max_ms;
|
||||||
|
double avg_ms;
|
||||||
|
double mdev_ms;
|
||||||
|
|
||||||
|
ping_stats_get(stats, &min_ms, &max_ms, &avg_ms, &mdev_ms);
|
||||||
|
printf("round-trip min/avg/max/mdev = %.3f/%.3f/%.3f/%.3f ms\n",
|
||||||
|
min_ms, avg_ms, max_ms, mdev_ms);
|
||||||
|
}
|
||||||
72
src/scheduler/scheduler.c
Normal file
72
src/scheduler/scheduler.c
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "ft_ping_flags.h"
|
||||||
|
#include "internal/scheduler.h"
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
static void sigalrm_handler(int sig);
|
||||||
|
static void sigint_handler(int sig);
|
||||||
|
static void install_signal(int signum, void (*handler)(int));
|
||||||
|
/* -------------------- */
|
||||||
|
|
||||||
|
static t_ping_state *g_state = NULL;
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_scheduler_init(t_ping_state *state)
|
||||||
|
{
|
||||||
|
g_state = state;
|
||||||
|
install_signal(SIGALRM, sigalrm_handler);
|
||||||
|
install_signal(SIGINT, sigint_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_scheduler_arm(const t_ping_config *config)
|
||||||
|
{
|
||||||
|
struct itimerval itv;
|
||||||
|
double interval;
|
||||||
|
|
||||||
|
if (HAS_FLAG(config->flags, FLAG_FLOOD))
|
||||||
|
return;
|
||||||
|
interval = (double)config->interval;
|
||||||
|
itv.it_value.tv_sec = (time_t)interval;
|
||||||
|
itv.it_value.tv_usec = (suseconds_t)((interval - (double)(time_t)interval)
|
||||||
|
* 1e6);
|
||||||
|
itv.it_interval.tv_sec = 0;
|
||||||
|
itv.it_interval.tv_usec = 0;
|
||||||
|
setitimer(ITIMER_REAL, &itv, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_scheduler_select_tv(const t_ping_config *config, struct timeval *tv)
|
||||||
|
{
|
||||||
|
tv->tv_usec = 0;
|
||||||
|
tv->tv_sec = (long)config->timeout * !HAS_FLAG(config->flags, FLAG_FLOOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sigalrm_handler(int sig)
|
||||||
|
{
|
||||||
|
(void)sig;
|
||||||
|
if (NULL != g_state)
|
||||||
|
g_state->send_flag = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sigint_handler(int sig)
|
||||||
|
{
|
||||||
|
(void)sig;
|
||||||
|
if (NULL != g_state)
|
||||||
|
g_state->stop_flag = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
install_signal(int signum, void (*handler)(int))
|
||||||
|
{
|
||||||
|
struct sigaction sa;
|
||||||
|
|
||||||
|
sa.sa_handler = handler;
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_flags = 0;
|
||||||
|
sigaction(signum, &sa, NULL);
|
||||||
|
}
|
||||||
64
src/stats/stats.c
Normal file
64
src/stats/stats.c
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "internal/stats.h"
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
static double compute_mdev_ms(const t_ping_stats *s, double avg_ms);
|
||||||
|
/* -------------------- */
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_stats_init(t_ping_stats *s)
|
||||||
|
{
|
||||||
|
s->min_ns = INT64_MAX;
|
||||||
|
s->max_ns = INT64_MIN;
|
||||||
|
s->sum_ns = 0;
|
||||||
|
s->sum_sq_ns = 0;
|
||||||
|
s->count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_stats_update(t_ping_stats *s, int64_t rtt_ns)
|
||||||
|
{
|
||||||
|
int64_t rtt_us;
|
||||||
|
|
||||||
|
if (rtt_ns < s->min_ns)
|
||||||
|
s->min_ns = rtt_ns;
|
||||||
|
if (rtt_ns > s->max_ns)
|
||||||
|
s->max_ns = rtt_ns;
|
||||||
|
s->sum_ns += rtt_ns;
|
||||||
|
rtt_us = rtt_ns / 1000;
|
||||||
|
s->sum_sq_ns += rtt_us * rtt_us;
|
||||||
|
s->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_stats_get(const t_ping_stats *s,
|
||||||
|
double *min_ms, double *max_ms, double *avg_ms, double *mdev_ms)
|
||||||
|
{
|
||||||
|
if (0 == s->count)
|
||||||
|
{
|
||||||
|
*min_ms = 0.0;
|
||||||
|
*max_ms = 0.0;
|
||||||
|
*avg_ms = 0.0;
|
||||||
|
*mdev_ms = 0.0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*min_ms = (double)s->min_ns / 1e6;
|
||||||
|
*max_ms = (double)s->max_ns / 1e6;
|
||||||
|
*avg_ms = (double)s->sum_ns / 1e6 / (double)s->count;
|
||||||
|
*mdev_ms = compute_mdev_ms(s, *avg_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
static double
|
||||||
|
compute_mdev_ms(const t_ping_stats *s, double avg_ms)
|
||||||
|
{
|
||||||
|
double avg_us;
|
||||||
|
double var_us2;
|
||||||
|
|
||||||
|
avg_us = avg_ms * 1000.0;
|
||||||
|
var_us2 = (double)s->sum_sq_ns / (double)s->count - avg_us * avg_us;
|
||||||
|
if (0.0 > var_us2)
|
||||||
|
var_us2 = 0.0;
|
||||||
|
return (sqrt(var_us2) / 1000.0);
|
||||||
|
}
|
||||||
9
src/tracker/init.c
Normal file
9
src/tracker/init.c
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "internal/tracker.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_tracker_init(t_ping_tracker *t)
|
||||||
|
{
|
||||||
|
memset(t, 0, sizeof(*t));
|
||||||
|
}
|
||||||
18
src/tracker/record_recv.c
Normal file
18
src/tracker/record_recv.c
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "icmp.h"
|
||||||
|
#include "internal/tracker.h"
|
||||||
|
|
||||||
|
int64_t
|
||||||
|
ping_tracker_record_recv(t_ping_tracker *t, uint16_t seq,
|
||||||
|
const struct timespec *ts)
|
||||||
|
{
|
||||||
|
t_ping_tracker_slot *slot;
|
||||||
|
|
||||||
|
slot = &t->slots[seq % PING_TRACKER_SLOTS];
|
||||||
|
if (false == slot->used || true == slot->acked)
|
||||||
|
return (-1);
|
||||||
|
slot->acked = true;
|
||||||
|
t->nb_recv++;
|
||||||
|
return (icmp_time_diff_ns(&slot->ts, ts));
|
||||||
|
}
|
||||||
16
src/tracker/record_send.c
Normal file
16
src/tracker/record_send.c
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "internal/tracker.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
ping_tracker_record_send(t_ping_tracker *t, uint16_t seq,
|
||||||
|
const struct timespec *ts)
|
||||||
|
{
|
||||||
|
t_ping_tracker_slot *slot;
|
||||||
|
|
||||||
|
slot = &t->slots[seq % PING_TRACKER_SLOTS];
|
||||||
|
slot->ts = *ts;
|
||||||
|
slot->used = true;
|
||||||
|
slot->acked = false;
|
||||||
|
t->nb_sent++;
|
||||||
|
}
|
||||||
64
tests/ping/stats/test_stats.c
Normal file
64
tests/ping/stats/test_stats.c
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "internal/stats.h"
|
||||||
|
|
||||||
|
Test(ping_stats, init_neutral)
|
||||||
|
{
|
||||||
|
t_ping_stats s;
|
||||||
|
double min_ms, max_ms, avg_ms, mdev_ms;
|
||||||
|
|
||||||
|
ping_stats_init(&s);
|
||||||
|
cr_assert_eq(s.count, (size_t)0);
|
||||||
|
ping_stats_get(&s, &min_ms, &max_ms, &avg_ms, &mdev_ms);
|
||||||
|
cr_assert_float_eq(min_ms, 0.0, 1e-9);
|
||||||
|
cr_assert_float_eq(max_ms, 0.0, 1e-9);
|
||||||
|
cr_assert_float_eq(avg_ms, 0.0, 1e-9);
|
||||||
|
cr_assert_float_eq(mdev_ms, 0.0, 1e-9);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(ping_stats, single_update)
|
||||||
|
{
|
||||||
|
t_ping_stats s;
|
||||||
|
double min_ms, max_ms, avg_ms, mdev_ms;
|
||||||
|
|
||||||
|
ping_stats_init(&s);
|
||||||
|
ping_stats_update(&s, 10000000LL); /* 10 ms */
|
||||||
|
ping_stats_get(&s, &min_ms, &max_ms, &avg_ms, &mdev_ms);
|
||||||
|
cr_assert_float_eq(min_ms, 10.0, 1e-6);
|
||||||
|
cr_assert_float_eq(max_ms, 10.0, 1e-6);
|
||||||
|
cr_assert_float_eq(avg_ms, 10.0, 1e-6);
|
||||||
|
cr_assert_float_eq(mdev_ms, 0.0, 1e-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(ping_stats, multiple_updates)
|
||||||
|
{
|
||||||
|
t_ping_stats s;
|
||||||
|
double min_ms, max_ms, avg_ms, mdev_ms;
|
||||||
|
|
||||||
|
ping_stats_init(&s);
|
||||||
|
ping_stats_update(&s, 10000000LL); /* 10 ms */
|
||||||
|
ping_stats_update(&s, 20000000LL); /* 20 ms */
|
||||||
|
ping_stats_update(&s, 30000000LL); /* 30 ms */
|
||||||
|
ping_stats_get(&s, &min_ms, &max_ms, &avg_ms, &mdev_ms);
|
||||||
|
cr_assert_float_eq(min_ms, 10.0, 1e-6);
|
||||||
|
cr_assert_float_eq(max_ms, 30.0, 1e-6);
|
||||||
|
cr_assert_float_eq(avg_ms, 20.0, 1e-6);
|
||||||
|
cr_assert(mdev_ms > 0.0, "mdev should be > 0 for different values");
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(ping_stats, identical_values_mdev_zero)
|
||||||
|
{
|
||||||
|
t_ping_stats s;
|
||||||
|
double min_ms, max_ms, avg_ms, mdev_ms;
|
||||||
|
|
||||||
|
ping_stats_init(&s);
|
||||||
|
ping_stats_update(&s, 15000000LL);
|
||||||
|
ping_stats_update(&s, 15000000LL);
|
||||||
|
ping_stats_update(&s, 15000000LL);
|
||||||
|
ping_stats_get(&s, &min_ms, &max_ms, &avg_ms, &mdev_ms);
|
||||||
|
cr_assert_float_eq(min_ms, 15.0, 1e-6);
|
||||||
|
cr_assert_float_eq(max_ms, 15.0, 1e-6);
|
||||||
|
cr_assert_float_eq(avg_ms, 15.0, 1e-6);
|
||||||
|
cr_assert_float_eq(mdev_ms, 0.0, 1e-3);
|
||||||
|
}
|
||||||
103
tests/ping/tracker/test_tracker.c
Normal file
103
tests/ping/tracker/test_tracker.c
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "internal/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)
|
||||||
|
{
|
||||||
|
t_ping_tracker t;
|
||||||
|
|
||||||
|
ping_tracker_init(&t);
|
||||||
|
cr_assert_eq(ping_tracker_sent(&t), (size_t)0);
|
||||||
|
cr_assert_eq(ping_tracker_recv(&t), (size_t)0);
|
||||||
|
cr_assert_eq(ping_tracker_lost(&t), (size_t)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(ping_tracker, basic_send_recv)
|
||||||
|
{
|
||||||
|
t_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, 1, &ts_send);
|
||||||
|
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(ping_tracker_sent(&t), (size_t)1);
|
||||||
|
cr_assert_eq(ping_tracker_recv(&t), (size_t)1);
|
||||||
|
cr_assert_eq(ping_tracker_lost(&t), (size_t)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(ping_tracker, double_recv_ignored)
|
||||||
|
{
|
||||||
|
t_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, 5000000);
|
||||||
|
ping_tracker_record_send(&t, 5, &ts_send);
|
||||||
|
rtt = ping_tracker_record_recv(&t, 5, &ts_recv);
|
||||||
|
cr_assert(rtt >= 0, "First recv should succeed");
|
||||||
|
rtt = ping_tracker_record_recv(&t, 5, &ts_recv);
|
||||||
|
cr_assert_eq(rtt, (int64_t)-1, "Duplicate recv should return -1");
|
||||||
|
cr_assert_eq(ping_tracker_recv(&t), (size_t)1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(ping_tracker, unknown_seq_returns_minus_one)
|
||||||
|
{
|
||||||
|
t_ping_tracker t;
|
||||||
|
struct timespec ts;
|
||||||
|
int64_t rtt;
|
||||||
|
|
||||||
|
ping_tracker_init(&t);
|
||||||
|
ts = make_ts(0, 0);
|
||||||
|
rtt = ping_tracker_record_recv(&t, 42, &ts);
|
||||||
|
cr_assert_eq(rtt, (int64_t)-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Test(ping_tracker, wraparound_seq)
|
||||||
|
{
|
||||||
|
t_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)
|
||||||
|
{
|
||||||
|
t_ping_tracker t;
|
||||||
|
struct timespec ts;
|
||||||
|
|
||||||
|
ping_tracker_init(&t);
|
||||||
|
ts = make_ts(0, 0);
|
||||||
|
ping_tracker_record_send(&t, 1, &ts);
|
||||||
|
ping_tracker_record_send(&t, 2, &ts);
|
||||||
|
ping_tracker_record_send(&t, 3, &ts);
|
||||||
|
ping_tracker_record_recv(&t, 2, &ts);
|
||||||
|
cr_assert_eq(ping_tracker_sent(&t), (size_t)3);
|
||||||
|
cr_assert_eq(ping_tracker_recv(&t), (size_t)1);
|
||||||
|
cr_assert_eq(ping_tracker_lost(&t), (size_t)2);
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue