Blame omping.c

Packit 61cb5a
/*
Packit 61cb5a
 * Copyright (c) 2010-2011, Red Hat, Inc.
Packit 61cb5a
 *
Packit 61cb5a
 * Permission to use, copy, modify, and/or distribute this software for any
Packit 61cb5a
 * purpose with or without fee is hereby granted, provided that the above
Packit 61cb5a
 * copyright notice and this permission notice appear in all copies.
Packit 61cb5a
 *
Packit 61cb5a
 * THE SOFTWARE IS PROVIDED "AS IS" AND RED HAT, INC. DISCLAIMS ALL WARRANTIES
Packit 61cb5a
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
Packit 61cb5a
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RED HAT, INC. BE LIABLE
Packit 61cb5a
 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
Packit 61cb5a
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
Packit 61cb5a
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
Packit 61cb5a
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Packit 61cb5a
 */
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Author: Jan Friesse <jfriesse@redhat.com>
Packit 61cb5a
 */
Packit 61cb5a
Packit 61cb5a
#include <sys/types.h>
Packit 61cb5a
Packit 61cb5a
#define __STDC_FORMAT_MACROS
Packit 61cb5a
#define __STDC_LIMIT_MACROS
Packit 61cb5a
#include <inttypes.h>
Packit 61cb5a
#include <err.h>
Packit 61cb5a
#include <signal.h>
Packit 61cb5a
#include <stdio.h>
Packit 61cb5a
#include <stdint.h>
Packit 61cb5a
#include <stdlib.h>
Packit 61cb5a
#include <string.h>
Packit 61cb5a
#include <unistd.h>
Packit 61cb5a
Packit 61cb5a
#include "addrfunc.h"
Packit 61cb5a
#include "cli.h"
Packit 61cb5a
#include "logging.h"
Packit 61cb5a
#include "msg.h"
Packit 61cb5a
#include "msgsend.h"
Packit 61cb5a
#include "omping.h"
Packit 61cb5a
#include "rhfunc.h"
Packit 61cb5a
#include "rsfunc.h"
Packit 61cb5a
#include "sockfunc.h"
Packit 61cb5a
#include "tlv.h"
Packit 61cb5a
#include "util.h"
Packit 61cb5a
Packit 61cb5a
#define MAX_EXIT_REQUESTS	2
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Structure with internal omping data
Packit 61cb5a
 */
Packit 61cb5a
struct omping_instance {
Packit 61cb5a
	struct ai_item	local_addr;
Packit 61cb5a
	struct ai_item	mcast_addr;
Packit 61cb5a
	struct rh_list	remote_hosts;
Packit 61cb5a
	struct ai_list	remote_addrs;
Packit 61cb5a
	enum omping_op_mode op_mode;
Packit 61cb5a
	enum sf_transport_method transport_method;
Packit 61cb5a
	char		*local_ifname;
Packit 61cb5a
	uint64_t	send_count_queries;
Packit 61cb5a
	int		auto_exit;
Packit 61cb5a
	int		cont_stat;
Packit 61cb5a
	int		dup_buf_items;
Packit 61cb5a
	int		hn_max_len;
Packit 61cb5a
	int		ip_ver;
Packit 61cb5a
	int		mcast_socket;
Packit 61cb5a
	int		quiet;
Packit 61cb5a
	int		rate_limit_time;
Packit 61cb5a
	int		rcvbuf_size;
Packit 61cb5a
	int		single_addr;
Packit 61cb5a
	int		sndbuf_size;
Packit 61cb5a
	int		timeout_time;
Packit 61cb5a
	int		ucast_socket;
Packit 61cb5a
	int		wait_for_finish_time;
Packit 61cb5a
	int		wait_time;
Packit 61cb5a
	unsigned int	rh_no_active;
Packit 61cb5a
	uint16_t	port;
Packit 61cb5a
	uint8_t		ttl;
Packit 61cb5a
};
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * User requested exit of application (usually with SIGINT)
Packit 61cb5a
 */
Packit 61cb5a
static int exit_requested;
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * User requested to display overall statistics (SIGINT/SIGUSR1)
Packit 61cb5a
 */
Packit 61cb5a
static int display_stats_requested;
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Function prototypes
Packit 61cb5a
 */
Packit 61cb5a
static int	get_packet_loss_percent(uint64_t packet_sent, uint64_t packet_received);
Packit 61cb5a
Packit 61cb5a
static int	omping_check_msg_common(const struct msg_decoded *msg_decoded);
Packit 61cb5a
Packit 61cb5a
static void	omping_client_move_to_stop(struct omping_instance *instance,
Packit 61cb5a
    struct rh_item *ri, enum rh_client_stop_reason stop_reason);
Packit 61cb5a
Packit 61cb5a
static void	omping_instance_create(struct omping_instance *instance, int argc,
Packit 61cb5a
    char *argv[]);
Packit 61cb5a
Packit 61cb5a
static void	omping_instance_free(struct omping_instance *instance);
Packit 61cb5a
Packit 61cb5a
static int	omping_poll_receive_loop(struct omping_instance *instance, int timeout_time);
Packit 61cb5a
Packit 61cb5a
static int	omping_poll_timeout(struct omping_instance *instance, struct timeval *old_tstamp,
Packit 61cb5a
    int timeout_time);
Packit 61cb5a
Packit 61cb5a
static int	omping_process_msg(struct omping_instance *instance, const char *msg,
Packit 61cb5a
    size_t msg_len, const struct sockaddr_storage *from, uint8_t ttl, enum sf_cast_type cast_type,
Packit 61cb5a
    struct timeval rp_timestamp);
Packit 61cb5a
Packit 61cb5a
static int	omping_process_answer_msg(struct omping_instance *instance, const char *msg,
Packit 61cb5a
    size_t msg_len, const struct msg_decoded *msg_decoded, const struct sockaddr_storage *from,
Packit 61cb5a
    uint8_t ttl, enum sf_cast_type cast_type, struct timeval rp_timestamp);
Packit 61cb5a
Packit 61cb5a
static int	omping_process_init_msg(struct omping_instance *instance, const char *msg,
Packit 61cb5a
    size_t msg_len, const struct msg_decoded *msg_decoded, const struct sockaddr_storage *from,
Packit 61cb5a
    struct timeval rp_timestamp);
Packit 61cb5a
Packit 61cb5a
static int	omping_process_query_msg(struct omping_instance *instance, const char *msg,
Packit 61cb5a
    size_t msg_len, const struct msg_decoded *msg_decoded, const struct sockaddr_storage *from,
Packit 61cb5a
    struct timeval rp_timestamp);
Packit 61cb5a
Packit 61cb5a
static int	omping_process_response_msg(struct omping_instance *instance, const char *msg,
Packit 61cb5a
    size_t msg_len, const struct msg_decoded *msg_decoded, const struct sockaddr_storage *from);
Packit 61cb5a
Packit 61cb5a
static int	omping_send_client_query(struct omping_instance *instance, struct rh_item *ri,
Packit 61cb5a
    int increase);
Packit 61cb5a
Packit 61cb5a
static int	omping_send_client_msgs(struct omping_instance *instance);
Packit 61cb5a
Packit 61cb5a
static void	omping_send_receive_loop(struct omping_instance *instance, int timeout_time,
Packit 61cb5a
    int final_stats, int allow_auto_exit);
Packit 61cb5a
Packit 61cb5a
static void	print_client_state(const char *host_name, int host_name_len,
Packit 61cb5a
    enum sf_transport_method transport_method, const struct sockaddr_storage *mcast_addr,
Packit 61cb5a
    const struct sockaddr_storage *remote_addr, enum rh_client_state state,
Packit 61cb5a
    enum rh_client_stop_reason stop_reason);
Packit 61cb5a
Packit 61cb5a
static void	print_final_remote_version(const struct rh_list *remote_hosts, int host_name_len);
Packit 61cb5a
Packit 61cb5a
static void	print_final_stats(const struct rh_list *remote_hosts, int host_name_len,
Packit 61cb5a
    enum sf_transport_method transport_method);
Packit 61cb5a
Packit 61cb5a
static void	print_packet_stats(const char *host_name, int host_name_len, uint32_t seq,
Packit 61cb5a
    int is_dup, size_t msg_len, int dist_set, uint8_t dist, int rtt_set, double rtt,
Packit 61cb5a
    double avg_rtt, int loss, enum sf_cast_type cast_type, int cont_stat);
Packit 61cb5a
Packit 61cb5a
static void	siginfo_handler(int sig);
Packit 61cb5a
static void	sigint_handler(int sig);
Packit 61cb5a
Packit 61cb5a
static void	register_signal_handlers(void);
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Functions implementation
Packit 61cb5a
 */
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Entry point of omping
Packit 61cb5a
 */
Packit 61cb5a
int
Packit 61cb5a
main(int argc, char *argv[])
Packit 61cb5a
{
Packit 61cb5a
	struct omping_instance instance;
Packit 61cb5a
	int allow_auto_exit;
Packit 61cb5a
	int final_stats;
Packit 61cb5a
	int wait_for_finish_time;
Packit 61cb5a
Packit 61cb5a
	omping_instance_create(&instance, argc, argv);
Packit 61cb5a
Packit 61cb5a
	register_signal_handlers();
Packit 61cb5a
Packit 61cb5a
	if (instance.op_mode == OMPING_OP_MODE_SERVER) {
Packit 61cb5a
		final_stats = allow_auto_exit = 0;
Packit 61cb5a
	} else {
Packit 61cb5a
		final_stats = allow_auto_exit = 1;
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	omping_send_receive_loop(&instance, instance.timeout_time, final_stats, allow_auto_exit);
Packit 61cb5a
Packit 61cb5a
	if (!instance.single_addr && instance.wait_for_finish_time != 0 &&
Packit 61cb5a
	    instance.op_mode != OMPING_OP_MODE_CLIENT) {
Packit 61cb5a
		exit_requested = 0;
Packit 61cb5a
Packit 61cb5a
		DEBUG_PRINTF("Moving all clients to stop state and server to finishing state");
Packit 61cb5a
		rh_list_put_to_finish_state(&instance.remote_hosts, RH_LFS_BOTH);
Packit 61cb5a
Packit 61cb5a
		if (instance.wait_for_finish_time == -1) {
Packit 61cb5a
			wait_for_finish_time = 0;
Packit 61cb5a
		} else {
Packit 61cb5a
			wait_for_finish_time = instance.wait_for_finish_time;
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		VERBOSE_PRINTF("Waiting for %d ms to inform other nodes about instance exit",
Packit 61cb5a
		    instance.wait_for_finish_time);
Packit 61cb5a
Packit 61cb5a
		omping_send_receive_loop(&instance, wait_for_finish_time, 0, 0);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	omping_instance_free(&instance);
Packit 61cb5a
Packit 61cb5a
	return 0;
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Compute packet loss in percent from number of send and received packets
Packit 61cb5a
 */
Packit 61cb5a
static int
Packit 61cb5a
get_packet_loss_percent(uint64_t packet_sent, uint64_t packet_received)
Packit 61cb5a
{
Packit 61cb5a
	int loss;
Packit 61cb5a
Packit 61cb5a
	if (packet_received > packet_sent) {
Packit 61cb5a
		DEBUG_PRINTF("packet_received > packet_sent");
Packit 61cb5a
		loss = 0;
Packit 61cb5a
	} else {
Packit 61cb5a
		loss = (int)((1.0 - (double)packet_received / (double)packet_sent) * 100.0);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	return (loss);
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Test basic message characteristics. Return 0 on success, and -1 on fail.
Packit 61cb5a
 */
Packit 61cb5a
static int
Packit 61cb5a
omping_check_msg_common(const struct msg_decoded *msg_decoded)
Packit 61cb5a
{
Packit 61cb5a
	if (msg_decoded->msg_type != MSG_TYPE_INIT && msg_decoded->msg_type != MSG_TYPE_RESPONSE &&
Packit 61cb5a
	    msg_decoded->msg_type != MSG_TYPE_QUERY && msg_decoded->msg_type != MSG_TYPE_ANSWER) {
Packit 61cb5a
		DEBUG_PRINTF("Unknown type %c (0x%X) of message", msg_decoded->msg_type,
Packit 61cb5a
		    msg_decoded->msg_type);
Packit 61cb5a
Packit 61cb5a
		return (-1);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (msg_decoded->version != 2) {
Packit 61cb5a
		DEBUG_PRINTF("Message version %d is not supported", msg_decoded->version);
Packit 61cb5a
Packit 61cb5a
		return (-1);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	return (0);
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Move client to stop state. Instance is omping instance, ri is pointer to remote host item from
Packit 61cb5a
 * remote hosts list and stop_reason is reason to stop.
Packit 61cb5a
 */
Packit 61cb5a
static void
Packit 61cb5a
omping_client_move_to_stop(struct omping_instance *instance, struct rh_item *ri,
Packit 61cb5a
    enum rh_client_stop_reason stop_reason)
Packit 61cb5a
{
Packit 61cb5a
	ri->client_info.state = RH_CS_STOP;
Packit 61cb5a
	instance->rh_no_active--;
Packit 61cb5a
Packit 61cb5a
	if (instance->quiet < 2) {
Packit 61cb5a
		print_client_state(ri->addr->host_name, instance->hn_max_len,
Packit 61cb5a
		    instance->transport_method, NULL, &ri->addr->sas,
Packit 61cb5a
		    RH_CS_STOP, stop_reason);
Packit 61cb5a
	}
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Create instance of omping. argc and argv are taken form main function. Result is stored in
Packit 61cb5a
 * instance parameter
Packit 61cb5a
 */
Packit 61cb5a
static void
Packit 61cb5a
omping_instance_create(struct omping_instance *instance, int argc, char *argv[])
Packit 61cb5a
{
Packit 61cb5a
	uint16_t bind_port;
Packit 61cb5a
Packit 61cb5a
	bind_port = 0;
Packit 61cb5a
	memset(instance, 0, sizeof(struct omping_instance));
Packit 61cb5a
Packit 61cb5a
	cli_parse(&instance->remote_addrs, argc, argv, &instance->local_ifname, &instance->ip_ver,
Packit 61cb5a
	    &instance->local_addr, &instance->wait_time, &instance->transport_method,
Packit 61cb5a
	    &instance->mcast_addr, &instance->port, &instance->ttl, &instance->single_addr,
Packit 61cb5a
	    &instance->quiet, &instance->cont_stat, &instance->timeout_time,
Packit 61cb5a
	    &instance->wait_for_finish_time, &instance->dup_buf_items, &instance->rate_limit_time,
Packit 61cb5a
	    &instance->sndbuf_size, &instance->rcvbuf_size, &instance->send_count_queries,
Packit 61cb5a
	    &instance->auto_exit, &instance->op_mode);
Packit 61cb5a
Packit 61cb5a
	rh_list_create(&instance->remote_hosts, &instance->remote_addrs, instance->dup_buf_items,
Packit 61cb5a
	    instance->rate_limit_time);
Packit 61cb5a
Packit 61cb5a
	instance->rh_no_active = rh_list_length(&instance->remote_hosts);
Packit 61cb5a
Packit 61cb5a
	instance->ucast_socket =
Packit 61cb5a
	    sf_create_unicast_socket(AF_CAST_SA(&instance->local_addr.sas), instance->ttl, 1,
Packit 61cb5a
	    instance->single_addr, instance->local_ifname, instance->transport_method, 1, 0,
Packit 61cb5a
	    instance->sndbuf_size, instance->rcvbuf_size,
Packit 61cb5a
	    (instance->op_mode == OMPING_OP_MODE_CLIENT ? &bind_port : NULL));
Packit 61cb5a
Packit 61cb5a
	if (instance->ucast_socket == -1) {
Packit 61cb5a
		err(1, "Can't create/bind unicast socket");
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	switch (instance->op_mode) {
Packit 61cb5a
	case OMPING_OP_MODE_SERVER:
Packit 61cb5a
		instance->mcast_socket = -1;
Packit 61cb5a
		rh_list_put_to_finish_state(&instance->remote_hosts, RH_LFS_CLIENT);
Packit 61cb5a
		break;
Packit 61cb5a
	case OMPING_OP_MODE_SHOW_VERSION:
Packit 61cb5a
		rh_list_put_to_finish_state(&instance->remote_hosts, RH_LFS_SERVER);
Packit 61cb5a
		break;
Packit 61cb5a
	case OMPING_OP_MODE_CLIENT:
Packit 61cb5a
		rh_list_put_to_finish_state(&instance->remote_hosts, RH_LFS_SERVER);
Packit 61cb5a
	case OMPING_OP_MODE_NORMAL:
Packit 61cb5a
		instance->mcast_socket =
Packit 61cb5a
		    sf_create_multicast_socket((struct sockaddr *)&instance->mcast_addr.sas,
Packit 61cb5a
			AF_CAST_SA(&instance->local_addr.sas), instance->local_ifname,
Packit 61cb5a
			instance->ttl, instance->single_addr, instance->transport_method,
Packit 61cb5a
			&instance->remote_addrs, 1, 0, instance->sndbuf_size,
Packit 61cb5a
			instance->rcvbuf_size,
Packit 61cb5a
			(instance->op_mode == OMPING_OP_MODE_CLIENT ? bind_port : 0));
Packit 61cb5a
Packit 61cb5a
		if (instance->mcast_socket == -1) {
Packit 61cb5a
			err(1, "Can't create/bind multicast socket");
Packit 61cb5a
		}
Packit 61cb5a
		break;
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	util_random_init(&instance->local_addr.sas);
Packit 61cb5a
Packit 61cb5a
	rh_list_gen_cid(&instance->remote_hosts, &instance->local_addr);
Packit 61cb5a
Packit 61cb5a
	instance->hn_max_len = rh_list_hn_max_len(&instance->remote_hosts);
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Free allocated memory of omping instance.
Packit 61cb5a
 */
Packit 61cb5a
static void
Packit 61cb5a
omping_instance_free(struct omping_instance *instance)
Packit 61cb5a
{
Packit 61cb5a
	af_ai_list_free(&instance->remote_addrs);
Packit 61cb5a
	rh_list_free(&instance->remote_hosts);
Packit 61cb5a
Packit 61cb5a
	free(instance->local_addr.host_name);
Packit 61cb5a
	free(instance->mcast_addr.host_name);
Packit 61cb5a
	free(instance->local_ifname);
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Loop for receiving messages for given time (instance->wait_time) and process them. Instance is
Packit 61cb5a
 * omping instance. timeout_time is maximum time to wait.
Packit 61cb5a
 * Function returns 0 on success, or -2 on EINTR.
Packit 61cb5a
 */
Packit 61cb5a
static int
Packit 61cb5a
omping_poll_receive_loop(struct omping_instance *instance, int timeout_time)
Packit 61cb5a
{
Packit 61cb5a
	char msg[MAX_MSG_SIZE];
Packit 61cb5a
	struct sockaddr_storage from;
Packit 61cb5a
	struct timeval old_tstamp;
Packit 61cb5a
	struct timeval rp_timestamp;
Packit 61cb5a
	enum sf_cast_type cast_type;
Packit 61cb5a
	int i;
Packit 61cb5a
	int poll_res;
Packit 61cb5a
	int receive_res;
Packit 61cb5a
	uint8_t ttl;
Packit 61cb5a
	int res;
Packit 61cb5a
Packit 61cb5a
	memset(&old_tstamp, 0, sizeof(old_tstamp));
Packit 61cb5a
Packit 61cb5a
	do {
Packit 61cb5a
		poll_res = omping_poll_timeout(instance, &old_tstamp, timeout_time);
Packit 61cb5a
		if (poll_res == -2) {
Packit 61cb5a
			return (-2);
Packit 61cb5a
			/* NOTREACHED */
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		for (i = 0; i < 2; i++) {
Packit 61cb5a
			receive_res = 0;
Packit 61cb5a
Packit 61cb5a
			if (i == 0 && poll_res & 1) {
Packit 61cb5a
				receive_res = rs_receive_msg(instance->ucast_socket, &from, msg,
Packit 61cb5a
				    sizeof(msg), &ttl, &rp_timestamp);
Packit 61cb5a
			}
Packit 61cb5a
Packit 61cb5a
			if (i == 1 && poll_res & 2) {
Packit 61cb5a
				receive_res = rs_receive_msg(instance->mcast_socket, &from, msg,
Packit 61cb5a
				    sizeof(msg), &ttl, &rp_timestamp);
Packit 61cb5a
			}
Packit 61cb5a
Packit 61cb5a
			switch (receive_res) {
Packit 61cb5a
			case -1:
Packit 61cb5a
				err(2, "Cannot receive message");
Packit 61cb5a
				/* NOTREACHED */
Packit 61cb5a
				break;
Packit 61cb5a
			case -2:
Packit 61cb5a
				return (-2);
Packit 61cb5a
				/* NOTREACHED */
Packit 61cb5a
				break;
Packit 61cb5a
			case -3:
Packit 61cb5a
				warn("Cannot receive message");
Packit 61cb5a
				break;
Packit 61cb5a
			case -4:
Packit 61cb5a
				VERBOSE_PRINTF("Received message too long");
Packit 61cb5a
				break;
Packit 61cb5a
			}
Packit 61cb5a
Packit 61cb5a
			if (receive_res > 0) {
Packit 61cb5a
				if (i == 0) {
Packit 61cb5a
					cast_type = SF_CT_UNI;
Packit 61cb5a
				} else {
Packit 61cb5a
					switch (instance->transport_method) {
Packit 61cb5a
					case SF_TM_ASM:
Packit 61cb5a
					case SF_TM_SSM:
Packit 61cb5a
						cast_type = SF_CT_MULTI;
Packit 61cb5a
						break;
Packit 61cb5a
					case SF_TM_IPBC:
Packit 61cb5a
						cast_type = SF_CT_BROAD;
Packit 61cb5a
						break;
Packit 61cb5a
					default:
Packit 61cb5a
						DEBUG_PRINTF("Internal error - unknown tm");
Packit 61cb5a
						errx(1, "Internal error - unknown tm");
Packit 61cb5a
						/* NOTREACHED */
Packit 61cb5a
					}
Packit 61cb5a
				}
Packit 61cb5a
Packit 61cb5a
				res = omping_process_msg(instance, msg, receive_res, &from, ttl,
Packit 61cb5a
				    cast_type, rp_timestamp);
Packit 61cb5a
Packit 61cb5a
				if (res == -2) {
Packit 61cb5a
					return (-2);
Packit 61cb5a
				}
Packit 61cb5a
			}
Packit 61cb5a
		}
Packit 61cb5a
	} while (poll_res > 0);
Packit 61cb5a
Packit 61cb5a
	return (0);
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Wait for messages on sockets. instance is omping_instance and old_tstamp is temporary variable
Packit 61cb5a
 * which must be set to zero on first call. Function handles EINTR for display statistics.
Packit 61cb5a
 * Function is wrapper on top of rs_poll_timeout, but handles -1 error code. Other return values
Packit 61cb5a
 * have same meaning. timeout_time is maximum time to wait
Packit 61cb5a
 */
Packit 61cb5a
static int
Packit 61cb5a
omping_poll_timeout(struct omping_instance *instance, struct timeval *old_tstamp, int timeout_time)
Packit 61cb5a
{
Packit 61cb5a
	int poll_res;
Packit 61cb5a
Packit 61cb5a
	do {
Packit 61cb5a
		poll_res = rs_poll_timeout(instance->ucast_socket, instance->mcast_socket,
Packit 61cb5a
		    timeout_time, old_tstamp);
Packit 61cb5a
Packit 61cb5a
		switch (poll_res) {
Packit 61cb5a
		case -1:
Packit 61cb5a
			err(2, "Cannot poll on sockets");
Packit 61cb5a
			/* NOTREACHED */
Packit 61cb5a
			break;
Packit 61cb5a
		case -2:
Packit 61cb5a
			if (display_stats_requested) {
Packit 61cb5a
				display_stats_requested = 0;
Packit 61cb5a
Packit 61cb5a
				if (instance->op_mode == OMPING_OP_MODE_SHOW_VERSION) {
Packit 61cb5a
					print_final_remote_version(&instance->remote_hosts,
Packit 61cb5a
					    instance->hn_max_len);
Packit 61cb5a
				} else {
Packit 61cb5a
					print_final_stats(&instance->remote_hosts,
Packit 61cb5a
					    instance->hn_max_len, instance->transport_method);
Packit 61cb5a
				}
Packit 61cb5a
Packit 61cb5a
				printf("\n");
Packit 61cb5a
Packit 61cb5a
				if (!exit_requested) {
Packit 61cb5a
					break;
Packit 61cb5a
				}
Packit 61cb5a
			}
Packit 61cb5a
Packit 61cb5a
			return (-2);
Packit 61cb5a
			/* NOTREACHED */
Packit 61cb5a
			break;
Packit 61cb5a
		}
Packit 61cb5a
	} while (poll_res < 0);
Packit 61cb5a
Packit 61cb5a
	return (poll_res);
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Process received message. Instance is omping instance, msg is received message with msg_len
Packit 61cb5a
 * length, from is source of message. ttl is packet Time-To-Live or 0, if that information was not
Packit 61cb5a
 * available. cast_type is type of packet received (unicast/multicast/broadcast). rp_timestamp
Packit 61cb5a
 * is receiving time of packet.
Packit 61cb5a
 * Function returns 0 on success or -2 on EINTR.
Packit 61cb5a
 */
Packit 61cb5a
static int
Packit 61cb5a
omping_process_msg(struct omping_instance *instance, const char *msg, size_t msg_len,
Packit 61cb5a
    const struct sockaddr_storage *from, uint8_t ttl, enum sf_cast_type cast_type,
Packit 61cb5a
    struct timeval rp_timestamp)
Packit 61cb5a
{
Packit 61cb5a
	char addr_str[INET6_ADDRSTRLEN];
Packit 61cb5a
	struct msg_decoded msg_decoded;
Packit 61cb5a
	const char *cast_str;
Packit 61cb5a
	struct rh_item *rh_item;
Packit 61cb5a
	int res;
Packit 61cb5a
Packit 61cb5a
	res = 0;
Packit 61cb5a
Packit 61cb5a
	msg_decode(msg, msg_len, &msg_decoded);
Packit 61cb5a
Packit 61cb5a
	cast_str = sf_cast_type_to_str(cast_type);
Packit 61cb5a
Packit 61cb5a
	af_sa_to_str((struct sockaddr *)from, addr_str);
Packit 61cb5a
	DEBUG_PRINTF("Received %scast message from %s type %c (0x%X), len %zu", cast_str, addr_str,
Packit 61cb5a
	    msg_decoded.msg_type, msg_decoded.msg_type, msg_len);
Packit 61cb5a
Packit 61cb5a
	if (omping_check_msg_common(&msg_decoded) == -1) {
Packit 61cb5a
		res = ms_stop(instance->ucast_socket, &instance->mcast_addr.sas, &msg_decoded,
Packit 61cb5a
		    from);
Packit 61cb5a
	} else {
Packit 61cb5a
		switch (msg_decoded.msg_type) {
Packit 61cb5a
		case MSG_TYPE_INIT:
Packit 61cb5a
			if (cast_type != SF_CT_UNI)
Packit 61cb5a
				goto error_unknown_mcast;
Packit 61cb5a
Packit 61cb5a
			if (instance->op_mode == OMPING_OP_MODE_CLIENT)
Packit 61cb5a
				goto error_unknown_msg_type;
Packit 61cb5a
Packit 61cb5a
			res = omping_process_init_msg(instance, msg, msg_len, &msg_decoded, from,
Packit 61cb5a
			    rp_timestamp);
Packit 61cb5a
			break;
Packit 61cb5a
		case MSG_TYPE_RESPONSE:
Packit 61cb5a
			if (cast_type != SF_CT_UNI)
Packit 61cb5a
				goto error_unknown_mcast;
Packit 61cb5a
Packit 61cb5a
			if (instance->op_mode == OMPING_OP_MODE_SERVER)
Packit 61cb5a
				goto error_unknown_msg_type;
Packit 61cb5a
Packit 61cb5a
			res = omping_process_response_msg(instance, msg, msg_len, &msg_decoded,
Packit 61cb5a
			    from);
Packit 61cb5a
			break;
Packit 61cb5a
		case MSG_TYPE_QUERY:
Packit 61cb5a
			if (cast_type != SF_CT_UNI)
Packit 61cb5a
				goto error_unknown_mcast;
Packit 61cb5a
Packit 61cb5a
			if (instance->op_mode == OMPING_OP_MODE_CLIENT)
Packit 61cb5a
				goto error_unknown_msg_type;
Packit 61cb5a
Packit 61cb5a
			res = omping_process_query_msg(instance, msg, msg_len, &msg_decoded, from,
Packit 61cb5a
			    rp_timestamp);
Packit 61cb5a
			break;
Packit 61cb5a
		case MSG_TYPE_ANSWER:
Packit 61cb5a
			if (instance->op_mode == OMPING_OP_MODE_SERVER && cast_type == SF_CT_UNI)
Packit 61cb5a
				goto error_unknown_msg_type;
Packit 61cb5a
Packit 61cb5a
			res = omping_process_answer_msg(instance, msg, msg_len, &msg_decoded, from,
Packit 61cb5a
			    ttl, cast_type, rp_timestamp);
Packit 61cb5a
			break;
Packit 61cb5a
		}
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	switch (res) {
Packit 61cb5a
	case -1:
Packit 61cb5a
		err(2, "Cannot send message");
Packit 61cb5a
		/* NOTREACHED */
Packit 61cb5a
		break;
Packit 61cb5a
	case -2:
Packit 61cb5a
		return (-2);
Packit 61cb5a
		/* NOTREACHED */
Packit 61cb5a
		break;
Packit 61cb5a
	case -3:
Packit 61cb5a
		warn("Send message error");
Packit 61cb5a
		rh_item = rh_list_find(&instance->remote_hosts, (const struct sockaddr *)from);
Packit 61cb5a
		if (rh_item == NULL) {
Packit 61cb5a
			DEBUG_PRINTF("Received message from unknown address");
Packit 61cb5a
		} else {
Packit 61cb5a
			rh_item->client_info.no_err_msgs++;
Packit 61cb5a
		}
Packit 61cb5a
		break;
Packit 61cb5a
	case -4:
Packit 61cb5a
		DEBUG_PRINTF("Cannot send message. Buffer too small");
Packit 61cb5a
		break;
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	return (0);
Packit 61cb5a
Packit 61cb5a
error_unknown_mcast:
Packit 61cb5a
	DEBUG_PRINTF("Received multicast message with invalid type %c (0x%X)",
Packit 61cb5a
	    msg_decoded.msg_type, msg_decoded.msg_type);
Packit 61cb5a
Packit 61cb5a
	return (0);
Packit 61cb5a
Packit 61cb5a
error_unknown_msg_type:
Packit 61cb5a
	DEBUG_PRINTF("Received message type %c (0x%X) which is not supported in given "
Packit 61cb5a
	    "operational mode", msg_decoded.msg_type, msg_decoded.msg_type);
Packit 61cb5a
Packit 61cb5a
	return (0);
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Function to test if packet is duplicate. ci is client item information, seq is sequential number
Packit 61cb5a
 * and cast_type is type of packet received (unicast/multicast/broadcast).
Packit 61cb5a
 * Function returns 0 if packet is not duplicate, otherwise 1.
Packit 61cb5a
 */
Packit 61cb5a
static int
Packit 61cb5a
is_dup_packet(const struct rh_item_ci *ci, uint32_t seq, enum sf_cast_type cast_type)
Packit 61cb5a
{
Packit 61cb5a
	int cast_index;
Packit 61cb5a
	int res;
Packit 61cb5a
Packit 61cb5a
	cast_index = (cast_type == SF_CT_UNI ? 0 : 1);
Packit 61cb5a
Packit 61cb5a
	if (ci->dup_buffer[cast_index][seq % ci->dup_buf_items] == seq) {
Packit 61cb5a
		res = 1;
Packit 61cb5a
	} else {
Packit 61cb5a
		ci->dup_buffer[cast_index][seq % ci->dup_buf_items] = seq;
Packit 61cb5a
Packit 61cb5a
		res = 0;
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	return (res);
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Process answer message. Instance is omping instance, msg is received message with msg_len length,
Packit 61cb5a
 * msg_decoded is decoded message, from is address of sender. ttl is Time-To-Live of packet. If ttl
Packit 61cb5a
 * is 0, it means that it was not possible to find out ttl. cast_type is type of packet received
Packit 61cb5a
 * (unicast/multicast/broadcast). rp_timestamp is receiving time of packet.
Packit 61cb5a
 * Function returns 0 on sucess, otherwise same error as rs_sendto or -4 if message cannot be
Packit 61cb5a
 * created (usually due to small message buffer), or -5 if message is invalid (not for us, message
Packit 61cb5a
 * without client_id, ...).
Packit 61cb5a
 */
Packit 61cb5a
static int
Packit 61cb5a
omping_process_answer_msg(struct omping_instance *instance, const char *msg, size_t msg_len,
Packit 61cb5a
    const struct msg_decoded *msg_decoded, const struct sockaddr_storage *from, uint8_t ttl,
Packit 61cb5a
    enum sf_cast_type cast_type, struct timeval rp_timestamp)
Packit 61cb5a
{
Packit 61cb5a
	struct rh_item *rh_item;
Packit 61cb5a
	double avg_rtt;
Packit 61cb5a
	double rtt;
Packit 61cb5a
	uint64_t received;
Packit 61cb5a
	uint64_t sent;
Packit 61cb5a
	int cast_index;
Packit 61cb5a
	int dist_set;
Packit 61cb5a
	int first_packet;
Packit 61cb5a
	int is_dup;
Packit 61cb5a
	int rtt_set;
Packit 61cb5a
	int loss;
Packit 61cb5a
	uint8_t dist;
Packit 61cb5a
Packit 61cb5a
	rh_item = rh_list_find(&instance->remote_hosts, (const struct sockaddr *)from);
Packit 61cb5a
	if (rh_item == NULL) {
Packit 61cb5a
		DEBUG_PRINTF("Received message from unknown address");
Packit 61cb5a
		return (-5);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (msg_decoded->client_id == NULL) {
Packit 61cb5a
		DEBUG_PRINTF("Message doesn't contain client id");
Packit 61cb5a
		return (-5);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (msg_decoded->client_id_len != CLIENTID_LEN ||
Packit 61cb5a
	    memcmp(msg_decoded->client_id, rh_item->client_info.client_id, CLIENTID_LEN) != 0) {
Packit 61cb5a
		DEBUG_PRINTF("Message doesn't contain our client id");
Packit 61cb5a
		return (-5);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (!msg_decoded->seq_num_isset) {
Packit 61cb5a
		DEBUG_PRINTF("Message doesn't contain seq num");
Packit 61cb5a
		return (-5);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (rh_item->client_info.state != RH_CS_QUERY) {
Packit 61cb5a
		DEBUG_PRINTF("Client is not in query state. Ignoring message");
Packit 61cb5a
		return (-5);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (ttl > 0 && msg_decoded->ttl > 0) {
Packit 61cb5a
		dist_set = 1;
Packit 61cb5a
		dist =  msg_decoded->ttl - ttl;
Packit 61cb5a
	} else {
Packit 61cb5a
		dist_set = dist = 0;
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (msg_decoded->client_tstamp_isset) {
Packit 61cb5a
		rtt_set = 1;
Packit 61cb5a
		rtt = util_time_double_absdiff_ns(msg_decoded->client_tstamp, rp_timestamp);
Packit 61cb5a
	} else {
Packit 61cb5a
		rtt_set = 0;
Packit 61cb5a
		rtt = 0;
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	avg_rtt = 0;
Packit 61cb5a
	cast_index = (cast_type == SF_CT_UNI ? 0 : 1);
Packit 61cb5a
	is_dup = 0;
Packit 61cb5a
Packit 61cb5a
	if (instance->dup_buf_items > 0) {
Packit 61cb5a
		is_dup = is_dup_packet(&rh_item->client_info, msg_decoded->seq_num, cast_type);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (is_dup) {
Packit 61cb5a
		if (rh_item->client_info.no_dups[cast_index] == ((uint64_t)~0)) {
Packit 61cb5a
			DEBUG_PRINTF("Number of received duplicates for %s exhausted.",
Packit 61cb5a
			    rh_item->addr->host_name);
Packit 61cb5a
		} else {
Packit 61cb5a
			rh_item->client_info.no_dups[cast_index]++;
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		received = rh_item->client_info.no_received[cast_index];
Packit 61cb5a
	} else {
Packit 61cb5a
		first_packet = (rh_item->client_info.no_received[cast_index] == 0);
Packit 61cb5a
Packit 61cb5a
		received = ++rh_item->client_info.no_received[cast_index];
Packit 61cb5a
Packit 61cb5a
		if (cast_index == 0) {
Packit 61cb5a
			rh_item->client_info.lru_seq_num = msg_decoded->seq_num;
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		if (cast_type != SF_CT_UNI && first_packet &&
Packit 61cb5a
		    !rh_item->client_info.seq_num_overflow) {
Packit 61cb5a
			rh_item->client_info.first_mcast_seq = msg_decoded->seq_num;
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		if (rtt_set) {
Packit 61cb5a
			util_ov_update(&rh_item->client_info.avg_rtt[cast_index],
Packit 61cb5a
			    &rh_item->client_info.m2_rtt[cast_index], rtt, received);
Packit 61cb5a
Packit 61cb5a
			if (first_packet) {
Packit 61cb5a
				rh_item->client_info.rtt_max[cast_index] = rtt;
Packit 61cb5a
				rh_item->client_info.rtt_min[cast_index] = rtt;
Packit 61cb5a
			} else {
Packit 61cb5a
				if (rtt > rh_item->client_info.rtt_max[cast_index]) {
Packit 61cb5a
					rh_item->client_info.rtt_max[cast_index] = rtt;
Packit 61cb5a
				}
Packit 61cb5a
Packit 61cb5a
				if (rtt < rh_item->client_info.rtt_min[cast_index]) {
Packit 61cb5a
					rh_item->client_info.rtt_min[cast_index] = rtt;
Packit 61cb5a
				}
Packit 61cb5a
			}
Packit 61cb5a
		}
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (instance->cont_stat) {
Packit 61cb5a
		sent = rh_item->client_info.no_sent;
Packit 61cb5a
Packit 61cb5a
		if (cast_type != SF_CT_UNI && rh_item->client_info.first_mcast_seq > 0) {
Packit 61cb5a
			sent = sent - rh_item->client_info.first_mcast_seq + 1;
Packit 61cb5a
		}
Packit 61cb5a
		loss = get_packet_loss_percent(sent, received);
Packit 61cb5a
		avg_rtt = rh_item->client_info.avg_rtt[cast_index] / UTIL_NSINMS;
Packit 61cb5a
	} else {
Packit 61cb5a
		loss = 0;
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (instance->quiet == 0) {
Packit 61cb5a
		print_packet_stats(rh_item->addr->host_name, instance->hn_max_len,
Packit 61cb5a
		    msg_decoded->seq_num, is_dup, msg_len, dist_set, dist, rtt_set,
Packit 61cb5a
		    rtt / UTIL_NSINMS, avg_rtt, loss, cast_type, instance->cont_stat);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	return (0);
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Process init messge. instance is omping_instance, msg is received message with msg_len length,
Packit 61cb5a
 * msg_decoded is decoded message and from is sockaddr of sender. rp_timestamp is receiving time
Packit 61cb5a
 * of packet.
Packit 61cb5a
 * Function returns 0 on sucess, otherwise same error as rs_sendto or -4 if message cannot be
Packit 61cb5a
 * created (usually due to small message buffer)
Packit 61cb5a
 */
Packit 61cb5a
static int
Packit 61cb5a
omping_process_init_msg(struct omping_instance *instance, const char *msg, size_t msg_len,
Packit 61cb5a
    const struct msg_decoded *msg_decoded, const struct sockaddr_storage *from,
Packit 61cb5a
    struct timeval rp_timestamp)
Packit 61cb5a
{
Packit 61cb5a
	struct rh_item *rh_item;
Packit 61cb5a
	struct tlv_iterator tlv_iter;
Packit 61cb5a
	int pref_found;
Packit 61cb5a
Packit 61cb5a
	rh_item = rh_list_find(&instance->remote_hosts, (const struct sockaddr *)from);
Packit 61cb5a
	if (rh_item == NULL) {
Packit 61cb5a
		DEBUG_PRINTF("Received message from unknown address");
Packit 61cb5a
Packit 61cb5a
		return (ms_stop(instance->ucast_socket, &instance->mcast_addr.sas,
Packit 61cb5a
		    msg_decoded, from));
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (rh_item->server_info.state == RH_SS_FINISHING) {
Packit 61cb5a
		DEBUG_PRINTF("We are in finishing state. Sending request to stop.");
Packit 61cb5a
Packit 61cb5a
		return (ms_stop(instance->ucast_socket, &instance->mcast_addr.sas,
Packit 61cb5a
		    msg_decoded, from));
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (!msg_decoded->mcast_prefix_isset) {
Packit 61cb5a
		DEBUG_PRINTF("Mcast prefix is not set");
Packit 61cb5a
Packit 61cb5a
		return (ms_response(instance->ucast_socket, &instance->mcast_addr.sas,
Packit 61cb5a
		    msg_decoded, from, 0, 1, NULL, 0));
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	pref_found = 0;
Packit 61cb5a
Packit 61cb5a
	tlv_iter_init(msg, msg_len, &tlv_iter);
Packit 61cb5a
	while (tlv_iter_next(&tlv_iter) == 0) {
Packit 61cb5a
		if (tlv_iter_get_type(&tlv_iter) == TLV_OPT_TYPE_MCAST_PREFIX) {
Packit 61cb5a
			if (tlv_iter_pref_eq(&tlv_iter, &instance->mcast_addr.sas)) {
Packit 61cb5a
				pref_found = 1;
Packit 61cb5a
Packit 61cb5a
				break;
Packit 61cb5a
			}
Packit 61cb5a
		}
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (!pref_found) {
Packit 61cb5a
		DEBUG_PRINTF("Can't find required prefix");
Packit 61cb5a
Packit 61cb5a
		return (ms_response(instance->ucast_socket, &instance->mcast_addr.sas, msg_decoded,
Packit 61cb5a
		    from, 0, 1, NULL, 0));
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (util_time_absdiff(rh_item->server_info.last_init_ts, rp_timestamp) <
Packit 61cb5a
	    DEFAULT_WAIT_TIME) {
Packit 61cb5a
		DEBUG_PRINTF("Time diff between two init messages too short. Ignoring message.");
Packit 61cb5a
		return (0);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	util_gen_sid(rh_item->server_info.ses_id);
Packit 61cb5a
	rh_item->server_info.state = RH_SS_ANSWER;
Packit 61cb5a
	rh_item->server_info.last_init_ts = rp_timestamp;
Packit 61cb5a
Packit 61cb5a
	return (ms_response(instance->ucast_socket, &instance->mcast_addr.sas, msg_decoded, from,
Packit 61cb5a
	    1, 0, rh_item->server_info.ses_id, SESSIONID_LEN));
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Process query msg. instance is omping instance, msg is received message with msg_len length,
Packit 61cb5a
 * msg_decoded is decoded message and from is sender of message. rp_timestamp is receiving time
Packit 61cb5a
 * of packet.
Packit 61cb5a
 * Function returns 0 on sucess, otherwise same error as rs_sendto or -4 if message cannot be
Packit 61cb5a
 * created (usually due to small message buffer)
Packit 61cb5a
 */
Packit 61cb5a
static int
Packit 61cb5a
omping_process_query_msg(struct omping_instance *instance, const char *msg, size_t msg_len,
Packit 61cb5a
    const struct msg_decoded *msg_decoded, const struct sockaddr_storage *from,
Packit 61cb5a
    struct timeval rp_timestamp)
Packit 61cb5a
{
Packit 61cb5a
	struct rh_item *rh_item;
Packit 61cb5a
Packit 61cb5a
	rh_item = rh_list_find(&instance->remote_hosts, (const struct sockaddr *)from);
Packit 61cb5a
	if (rh_item == NULL) {
Packit 61cb5a
		DEBUG_PRINTF("Received message from unknown address");
Packit 61cb5a
Packit 61cb5a
		return (ms_stop(instance->ucast_socket, &instance->mcast_addr.sas,
Packit 61cb5a
		    msg_decoded, from));
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (rh_item->server_info.state != RH_SS_ANSWER) {
Packit 61cb5a
		DEBUG_PRINTF("Server is not in answer state");
Packit 61cb5a
Packit 61cb5a
		return (ms_stop(instance->ucast_socket, &instance->mcast_addr.sas,
Packit 61cb5a
		    msg_decoded, from));
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (!msg_decoded->seq_num_isset || msg_decoded->mcast_grp == NULL) {
Packit 61cb5a
		DEBUG_PRINTF("Received message doesn't have mcast group set");
Packit 61cb5a
Packit 61cb5a
		return (ms_stop(instance->ucast_socket, &instance->mcast_addr.sas,
Packit 61cb5a
		    msg_decoded, from));
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (msg_decoded->ses_id_len != SESSIONID_LEN ||
Packit 61cb5a
	    memcmp(msg_decoded->ses_id, rh_item->server_info.ses_id, SESSIONID_LEN) != 0) {
Packit 61cb5a
		DEBUG_PRINTF("Received message session id isn't expected");
Packit 61cb5a
Packit 61cb5a
		return (ms_stop(instance->ucast_socket, &instance->mcast_addr.sas,
Packit 61cb5a
		    msg_decoded, from));
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	/*
Packit 61cb5a
	 * Rate limiting
Packit 61cb5a
	 */
Packit 61cb5a
	if (instance->rate_limit_time > 0) {
Packit 61cb5a
		if (gcra_rl(&rh_item->server_info.gcra, rp_timestamp) == 0) {
Packit 61cb5a
			DEBUG_PRINTF("Received message rate limited");
Packit 61cb5a
			return (0);
Packit 61cb5a
		}
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	/*
Packit 61cb5a
	 * Answer to query message
Packit 61cb5a
	 */
Packit 61cb5a
	return (ms_answer(instance->ucast_socket, &instance->mcast_addr.sas, msg, msg_len,
Packit 61cb5a
	    msg_decoded, from, instance->ttl, MS_ANSWER_BOTH));
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Process response message. Instance is omping instance, msg is received message with msg_len
Packit 61cb5a
 * length, msg_decoded is decoded message and from is address of sender.
Packit 61cb5a
 * Function returns 0 on sucess, otherwise same error as rs_sendto or -4 if message cannot be
Packit 61cb5a
 * created (usually due to small message buffer), or -5 if message is invalid (not for us, message
Packit 61cb5a
 * without client_id, ...).
Packit 61cb5a
 */
Packit 61cb5a
static int
Packit 61cb5a
omping_process_response_msg(struct omping_instance *instance, const char *msg, size_t msg_len,
Packit 61cb5a
    const struct msg_decoded *msg_decoded, const struct sockaddr_storage *from)
Packit 61cb5a
{
Packit 61cb5a
	struct rh_item *rh_item;
Packit 61cb5a
	enum rh_client_state old_cstate;
Packit 61cb5a
	const char *ci_ses_id;
Packit 61cb5a
	const char *msg_ses_id;
Packit 61cb5a
	int send_res;
Packit 61cb5a
Packit 61cb5a
	rh_item = rh_list_find(&instance->remote_hosts, (const struct sockaddr *)from);
Packit 61cb5a
	if (rh_item == NULL) {
Packit 61cb5a
		DEBUG_PRINTF("Received message from unknown address");
Packit 61cb5a
Packit 61cb5a
		return (-5);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (rh_item->client_info.state == RH_CS_STOP) {
Packit 61cb5a
		DEBUG_PRINTF("Client is in stop state. Ignoring message.");
Packit 61cb5a
Packit 61cb5a
		return (-5);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (msg_decoded->client_id == NULL) {
Packit 61cb5a
		DEBUG_PRINTF("Message doesn't contain client id");
Packit 61cb5a
Packit 61cb5a
		return (-5);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (msg_decoded->client_id_len != CLIENTID_LEN ||
Packit 61cb5a
	    memcmp(msg_decoded->client_id, rh_item->client_info.client_id, CLIENTID_LEN) != 0) {
Packit 61cb5a
		DEBUG_PRINTF("Message doesn't contain our client id");
Packit 61cb5a
Packit 61cb5a
		return (-5);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (instance->op_mode == OMPING_OP_MODE_SHOW_VERSION) {
Packit 61cb5a
		if (msg_decoded->server_info_len > 0) {
Packit 61cb5a
			rh_item->client_info.server_info_len = msg_decoded->server_info_len;
Packit 61cb5a
Packit 61cb5a
			free(rh_item->client_info.server_info);
Packit 61cb5a
Packit 61cb5a
			rh_item->client_info.server_info =
Packit 61cb5a
			    (char *)malloc(rh_item->client_info.server_info_len);
Packit 61cb5a
Packit 61cb5a
			if (rh_item->client_info.server_info == NULL) {
Packit 61cb5a
				errx(1, "Can't alloc memory");
Packit 61cb5a
			}
Packit 61cb5a
Packit 61cb5a
			memcpy(rh_item->client_info.server_info, msg_decoded->server_info,
Packit 61cb5a
			    rh_item->client_info.server_info_len);
Packit 61cb5a
Packit 61cb5a
			omping_client_move_to_stop(instance, rh_item,
Packit 61cb5a
			    RH_CSR_REMOTE_VERSION_RECEIVED);
Packit 61cb5a
		} else {
Packit 61cb5a
			DEBUG_PRINTF("Message doesn't contain server information");
Packit 61cb5a
Packit 61cb5a
			return (-5);
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		return (0);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (msg_decoded->mcast_grp == NULL || msg_decoded->mcast_grp_len == 0) {
Packit 61cb5a
		DEBUG_PRINTF("Server doesn't send us multicast group");
Packit 61cb5a
Packit 61cb5a
		if (rh_item->client_info.state == RH_CS_QUERY) {
Packit 61cb5a
			DEBUG_PRINTF("Client was in query state. Put to initial state");
Packit 61cb5a
Packit 61cb5a
			rh_item->client_info.state = RH_CS_INITIAL;
Packit 61cb5a
			/*
Packit 61cb5a
			 * Technically, packet was sent and also received so no lost at all
Packit 61cb5a
			 */
Packit 61cb5a
			rh_item->client_info.no_sent--;
Packit 61cb5a
Packit 61cb5a
			util_gen_cid(rh_item->client_info.client_id, &instance->local_addr);
Packit 61cb5a
		} else {
Packit 61cb5a
			DEBUG_PRINTF("Client was not in query state. Put it to stop state");
Packit 61cb5a
			omping_client_move_to_stop(instance, rh_item, RH_CSR_SERVER);
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		return (-5);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (!(tlv_mcast_grp_eq(&instance->mcast_addr.sas, msg_decoded->mcast_grp,
Packit 61cb5a
	    msg_decoded->mcast_grp_len))) {
Packit 61cb5a
		DEBUG_PRINTF("Server send us different multicast group then expected");
Packit 61cb5a
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (msg_decoded->ses_id == NULL) {
Packit 61cb5a
		DEBUG_PRINTF("Message doesn't contain session id");
Packit 61cb5a
Packit 61cb5a
		return (-5);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (rh_item->client_info.ses_id_len == msg_decoded->ses_id_len) {
Packit 61cb5a
		ci_ses_id = rh_item->client_info.ses_id;
Packit 61cb5a
		msg_ses_id = msg_decoded->ses_id;
Packit 61cb5a
Packit 61cb5a
		if (memcmp(ci_ses_id, msg_ses_id, msg_decoded->ses_id_len) == 0) {
Packit 61cb5a
			DEBUG_PRINTF("Duplicate server response");
Packit 61cb5a
Packit 61cb5a
			return (-5);
Packit 61cb5a
		}
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	old_cstate = rh_item->client_info.state;
Packit 61cb5a
	rh_item->client_info.state = RH_CS_QUERY;
Packit 61cb5a
	rh_item->client_info.ses_id_len = msg_decoded->ses_id_len;
Packit 61cb5a
Packit 61cb5a
	free(rh_item->client_info.ses_id);
Packit 61cb5a
Packit 61cb5a
	rh_item->client_info.ses_id = (char *)malloc(rh_item->client_info.ses_id_len);
Packit 61cb5a
	if (rh_item->client_info.ses_id == NULL) {
Packit 61cb5a
		errx(1, "Can't alloc memory");
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	memcpy(rh_item->client_info.ses_id, msg_decoded->ses_id, rh_item->client_info.ses_id_len);
Packit 61cb5a
Packit 61cb5a
	if (old_cstate == RH_CS_INITIAL) {
Packit 61cb5a
		if (instance->quiet < 2) {
Packit 61cb5a
			print_client_state(rh_item->addr->host_name, instance->hn_max_len,
Packit 61cb5a
			    instance->transport_method, &instance->mcast_addr.sas,
Packit 61cb5a
			    &rh_item->addr->sas, RH_CS_QUERY, RH_CSR_NONE);
Packit 61cb5a
		}
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	send_res = omping_send_client_query(instance, rh_item, (old_cstate == RH_CS_INITIAL));
Packit 61cb5a
Packit 61cb5a
	return (send_res);
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Send client query message. instance is omping instance. ri is one item fro rh_list and it's
Packit 61cb5a
 * client to process. increase is boolean variable. If set, seq_num and no_sent packets are
Packit 61cb5a
 * increased.
Packit 61cb5a
 * Function return 0 on success, otherwise same error as rs_sendto or -4 if message cannot be
Packit 61cb5a
 * created (usually due to small message buffer)
Packit 61cb5a
 */
Packit 61cb5a
static int
Packit 61cb5a
omping_send_client_query(struct omping_instance *instance, struct rh_item *ri, int increase)
Packit 61cb5a
{
Packit 61cb5a
	struct rh_item_ci *ci;
Packit 61cb5a
	int send_res;
Packit 61cb5a
Packit 61cb5a
	ci = &ri->client_info;
Packit 61cb5a
Packit 61cb5a
	if (increase) {
Packit 61cb5a
		if (ci->no_sent + 1 == ((uint64_t)~0)) {
Packit 61cb5a
			omping_client_move_to_stop(instance, ri, RH_CSR_SEND_MAXIMUM);
Packit 61cb5a
			DEBUG_PRINTF("Maximum number of sent messages for %s exhausted. "
Packit 61cb5a
			    "Moving to stop state.", ri->addr->host_name);
Packit 61cb5a
Packit 61cb5a
			return (0);
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		if (instance->send_count_queries > 0 &&
Packit 61cb5a
		    ci->no_sent + 1 > instance->send_count_queries) {
Packit 61cb5a
			omping_client_move_to_stop(instance, ri, RH_CSR_TO_SEND_EXHAUSTED);
Packit 61cb5a
			DEBUG_PRINTF("Number of messages to be sent by %s exhausted. "
Packit 61cb5a
			    "Moving to stop state.", ri->addr->host_name);
Packit 61cb5a
Packit 61cb5a
			return (0);
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		ci->seq_num++;
Packit 61cb5a
		ci->no_sent++;
Packit 61cb5a
Packit 61cb5a
		if (ci->seq_num == 0) {
Packit 61cb5a
			ci->seq_num_overflow = 1;
Packit 61cb5a
			ci->seq_num++;
Packit 61cb5a
		}
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	send_res = ms_query(instance->ucast_socket, &ri->addr->sas, &instance->mcast_addr.sas,
Packit 61cb5a
	    ci->seq_num, ci->client_id, ci->ses_id, ci->ses_id_len);
Packit 61cb5a
Packit 61cb5a
	return (send_res);
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Send client init or request messages to all of remote hosts. instance is omping instance.
Packit 61cb5a
 * Function return 0 on success, or -2 on EINTR.
Packit 61cb5a
 */
Packit 61cb5a
static int
Packit 61cb5a
omping_send_client_msgs(struct omping_instance *instance)
Packit 61cb5a
{
Packit 61cb5a
	struct rh_item *remote_host;
Packit 61cb5a
	struct rh_item_ci *ci;
Packit 61cb5a
	int send_res;
Packit 61cb5a
Packit 61cb5a
	TAILQ_FOREACH(remote_host, &instance->remote_hosts, entries) {
Packit 61cb5a
		send_res = 0;
Packit 61cb5a
		ci = &remote_host->client_info;
Packit 61cb5a
Packit 61cb5a
		switch (ci->state) {
Packit 61cb5a
		case RH_CS_INITIAL:
Packit 61cb5a
			/*
Packit 61cb5a
			 * Initial message is send at most after DEFAULT_WAIT_TIME
Packit 61cb5a
			 */
Packit 61cb5a
			if (util_time_absdiff(ci->last_init_ts, util_get_time()) >
Packit 61cb5a
			    DEFAULT_WAIT_TIME) {
Packit 61cb5a
				if (instance->quiet < 2) {
Packit 61cb5a
					print_client_state(remote_host->addr->host_name,
Packit 61cb5a
					    instance->hn_max_len, instance->transport_method, NULL,
Packit 61cb5a
					    &remote_host->addr->sas, RH_CS_INITIAL, RH_CSR_NONE);
Packit 61cb5a
				}
Packit 61cb5a
Packit 61cb5a
				send_res = ms_init(instance->ucast_socket, &remote_host->addr->sas,
Packit 61cb5a
				    &instance->mcast_addr.sas, ci->client_id,
Packit 61cb5a
				    (instance->op_mode == OMPING_OP_MODE_SHOW_VERSION ? 1 : 0));
Packit 61cb5a
Packit 61cb5a
				ci->last_init_ts = util_get_time();
Packit 61cb5a
			}
Packit 61cb5a
			break;
Packit 61cb5a
		case RH_CS_QUERY:
Packit 61cb5a
			if (instance->wait_time == 0) {
Packit 61cb5a
				/*
Packit 61cb5a
				 * Handle wait time zero specifically. Send query if answer for
Packit 61cb5a
				 * previous query received or after 1ms.
Packit 61cb5a
				 */
Packit 61cb5a
				if (ci->lru_seq_num == ci->seq_num ||
Packit 61cb5a
				    util_time_absdiff(ci->last_query_ts, util_get_time()) >= 1) {
Packit 61cb5a
					send_res = omping_send_client_query(instance, remote_host,
Packit 61cb5a
					    1);
Packit 61cb5a
Packit 61cb5a
					ci->last_query_ts = util_get_time();
Packit 61cb5a
				}
Packit 61cb5a
			} else {
Packit 61cb5a
				send_res = omping_send_client_query(instance, remote_host, 1);
Packit 61cb5a
			}
Packit 61cb5a
			break;
Packit 61cb5a
		case RH_CS_STOP:
Packit 61cb5a
			/*
Packit 61cb5a
			 * Do nothing
Packit 61cb5a
			 */
Packit 61cb5a
			break;
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		switch (send_res) {
Packit 61cb5a
		case -1:
Packit 61cb5a
			err(2, "Cannot send message");
Packit 61cb5a
			/* NOTREACHED */
Packit 61cb5a
			break;
Packit 61cb5a
		case -2:
Packit 61cb5a
			return (-2);
Packit 61cb5a
			/* NOTREACHED */
Packit 61cb5a
			break;
Packit 61cb5a
		case -3:
Packit 61cb5a
			warn("Send message error");
Packit 61cb5a
			ci->no_err_msgs++;
Packit 61cb5a
			break;
Packit 61cb5a
		case -4:
Packit 61cb5a
			DEBUG_PRINTF("Cannot send message. Buffer too small");
Packit 61cb5a
			break;
Packit 61cb5a
		}
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	return (0);
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Main loop of omping. It is used for receiving and sending messages. On the end, it prints final
Packit 61cb5a
 * statistics. instance is omping instance. timeout_time is maximum amount of time to keep loop
Packit 61cb5a
 * running (after this time, loop is ended). final_stats is boolean flag which determines if final
Packit 61cb5a
 * statistics should be displayed or not. allow_auto_exit is boolean which if set, allows auto exit
Packit 61cb5a
 * if every client is in STOP state.
Packit 61cb5a
 */
Packit 61cb5a
static void
Packit 61cb5a
omping_send_receive_loop(struct omping_instance *instance, int timeout_time, int final_stats,
Packit 61cb5a
    int allow_auto_exit)
Packit 61cb5a
{
Packit 61cb5a
	struct timeval start_time;
Packit 61cb5a
	int clients_res;
Packit 61cb5a
	int loop_end;
Packit 61cb5a
	int poll_rec_res;
Packit 61cb5a
	int receive_timeout;
Packit 61cb5a
	uint64_t time_diff;
Packit 61cb5a
Packit 61cb5a
	if (timeout_time != 0) {
Packit 61cb5a
		start_time = util_get_time();
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	loop_end = 0;
Packit 61cb5a
Packit 61cb5a
	do {
Packit 61cb5a
		clients_res = omping_send_client_msgs(instance);
Packit 61cb5a
		if (clients_res != 0 && clients_res != -2) {
Packit 61cb5a
			err(3, "unknown value of clients_res %u", clients_res);
Packit 61cb5a
			/* NOTREACHED */
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		if (clients_res == -2) {
Packit 61cb5a
			if (exit_requested) {
Packit 61cb5a
				loop_end = 1;
Packit 61cb5a
			}
Packit 61cb5a
Packit 61cb5a
			continue;
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		if (timeout_time != 0) {
Packit 61cb5a
			time_diff = util_time_absdiff(start_time, util_get_time());
Packit 61cb5a
Packit 61cb5a
			if ((int)time_diff + instance->wait_time > timeout_time) {
Packit 61cb5a
				receive_timeout = timeout_time - time_diff;
Packit 61cb5a
			} else {
Packit 61cb5a
				receive_timeout = instance->wait_time;
Packit 61cb5a
			}
Packit 61cb5a
		} else {
Packit 61cb5a
			receive_timeout = instance->wait_time;
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		poll_rec_res = omping_poll_receive_loop(instance, receive_timeout);
Packit 61cb5a
Packit 61cb5a
		if (poll_rec_res != 0 && poll_rec_res != -2) {
Packit 61cb5a
			err(3, "unknown value of poll_rec_res %u", poll_rec_res);
Packit 61cb5a
			/* NOTREACHED */
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		if (exit_requested) {
Packit 61cb5a
			loop_end = 1;
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		if (timeout_time != 0 &&
Packit 61cb5a
		    (int)util_time_absdiff(start_time, util_get_time()) >= timeout_time) {
Packit 61cb5a
			loop_end = 1;
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		if (allow_auto_exit && instance->auto_exit && instance->rh_no_active == 0) {
Packit 61cb5a
			loop_end = 1;
Packit 61cb5a
		}
Packit 61cb5a
	} while (!loop_end);
Packit 61cb5a
Packit 61cb5a
	if (final_stats) {
Packit 61cb5a
		if (instance->op_mode == OMPING_OP_MODE_SHOW_VERSION) {
Packit 61cb5a
			print_final_remote_version(&instance->remote_hosts, instance->hn_max_len);
Packit 61cb5a
		} else {
Packit 61cb5a
			print_final_stats(&instance->remote_hosts, instance->hn_max_len,
Packit 61cb5a
			    instance->transport_method);
Packit 61cb5a
		}
Packit 61cb5a
	}
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Print status of client with host_name (maximum length of host_name_len). transport_method is
Packit 61cb5a
 * transport method to be used, mcast_addr is current multicast address to be used by client.
Packit 61cb5a
 * remote_addr is address of client and state is current state of client.
Packit 61cb5a
 */
Packit 61cb5a
static void
Packit 61cb5a
print_client_state(const char *host_name, int host_name_len,
Packit 61cb5a
    enum sf_transport_method transport_method, const struct sockaddr_storage *mcast_addr,
Packit 61cb5a
    const struct sockaddr_storage *remote_addr, enum rh_client_state state,
Packit 61cb5a
    enum rh_client_stop_reason stop_reason)
Packit 61cb5a
{
Packit 61cb5a
	char mcast_addr_str[INET6_ADDRSTRLEN];
Packit 61cb5a
	char rh_addr_str[INET6_ADDRSTRLEN];
Packit 61cb5a
Packit 61cb5a
	printf("%-*s : ", host_name_len, host_name);
Packit 61cb5a
Packit 61cb5a
	switch (state) {
Packit 61cb5a
	case RH_CS_INITIAL:
Packit 61cb5a
		printf("waiting for response msg");
Packit 61cb5a
		break;
Packit 61cb5a
	case RH_CS_QUERY:
Packit 61cb5a
		memset(mcast_addr_str, 0, sizeof(mcast_addr_str));
Packit 61cb5a
		memset(rh_addr_str, 0, sizeof(rh_addr_str));
Packit 61cb5a
Packit 61cb5a
		if (mcast_addr != NULL) {
Packit 61cb5a
			af_sa_to_str(AF_CAST_SA(mcast_addr), mcast_addr_str);
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		if (remote_addr != NULL) {
Packit 61cb5a
			af_sa_to_str(AF_CAST_SA(remote_addr), rh_addr_str);
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		switch (transport_method) {
Packit 61cb5a
		case SF_TM_ASM:
Packit 61cb5a
			printf("joined (S,G) = (*, %s), pinging", mcast_addr_str);
Packit 61cb5a
			break;
Packit 61cb5a
		case SF_TM_SSM:
Packit 61cb5a
			printf("joined (S,G) = (%s, %s), pinging", rh_addr_str, mcast_addr_str);
Packit 61cb5a
			break;
Packit 61cb5a
		case SF_TM_IPBC:
Packit 61cb5a
			printf("joined (S,G) = (*, %s), pinging", mcast_addr_str);
Packit 61cb5a
			break;
Packit 61cb5a
		}
Packit 61cb5a
		break;
Packit 61cb5a
	case RH_CS_STOP:
Packit 61cb5a
		switch (stop_reason) {
Packit 61cb5a
		case RH_CSR_NONE:
Packit 61cb5a
			DEBUG_PRINTF("internal program error.");
Packit 61cb5a
			errx(1, "Internal program error");
Packit 61cb5a
			break;
Packit 61cb5a
		case RH_CSR_SERVER:
Packit 61cb5a
			printf("server told us to stop");
Packit 61cb5a
			break;
Packit 61cb5a
		case RH_CSR_SEND_MAXIMUM:
Packit 61cb5a
			printf("maximum number of query messages exhausted");
Packit 61cb5a
			break;
Packit 61cb5a
		case RH_CSR_TO_SEND_EXHAUSTED:
Packit 61cb5a
			printf("given amount of query messages was sent");
Packit 61cb5a
			break;
Packit 61cb5a
		case RH_CSR_REMOTE_VERSION_RECEIVED:
Packit 61cb5a
			printf("remote version received");
Packit 61cb5a
			break;
Packit 61cb5a
		}
Packit 61cb5a
		break;
Packit 61cb5a
	}
Packit 61cb5a
	printf("\n");
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Print final remote versions. remote_hosts is list with all remote hosts and host_name_len is
Packit 61cb5a
 * maximal length of host name in list.
Packit 61cb5a
 */
Packit 61cb5a
static void
Packit 61cb5a
print_final_remote_version(const struct rh_list *remote_hosts, int host_name_len)
Packit 61cb5a
{
Packit 61cb5a
	struct rh_item *rh_item;
Packit 61cb5a
	struct rh_item_ci *ci;
Packit 61cb5a
	size_t i;
Packit 61cb5a
	unsigned char ch;
Packit 61cb5a
Packit 61cb5a
	printf("\n");
Packit 61cb5a
Packit 61cb5a
	TAILQ_FOREACH(rh_item, remote_hosts, entries) {
Packit 61cb5a
			ci = &rh_item->client_info;
Packit 61cb5a
Packit 61cb5a
			printf("%-*s : ", host_name_len, rh_item->addr->host_name);
Packit 61cb5a
Packit 61cb5a
			if (ci->server_info_len == 0) {
Packit 61cb5a
				printf("response message not received\n");
Packit 61cb5a
			} else {
Packit 61cb5a
				for (i = 0; i < ci->server_info_len; i++) {
Packit 61cb5a
					ch = ci->server_info[i];
Packit 61cb5a
Packit 61cb5a
				if (ch >= ' ' && ch < 0x7f && ch != '\\') {
Packit 61cb5a
					fputc(ch, stdout);
Packit 61cb5a
				} else {
Packit 61cb5a
					if (ch == '\\') {
Packit 61cb5a
						printf("\\\\");
Packit 61cb5a
					} else {
Packit 61cb5a
						printf("\\x%02X", ch);
Packit 61cb5a
					}
Packit 61cb5a
				}
Packit 61cb5a
			}
Packit 61cb5a
Packit 61cb5a
			printf("\n");
Packit 61cb5a
		}
Packit 61cb5a
	}
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Print final statistics. remote_hosts is list with all remote hosts and host_name_len is maximal
Packit 61cb5a
 * length of host name in list. transport_method is transport method (SF_TM_ASM/SSM/IPBC) from
Packit 61cb5a
 * omping instance.
Packit 61cb5a
 */
Packit 61cb5a
static void
Packit 61cb5a
print_final_stats(const struct rh_list *remote_hosts, int host_name_len,
Packit 61cb5a
    enum sf_transport_method transport_method)
Packit 61cb5a
{
Packit 61cb5a
	const char *cast_str;
Packit 61cb5a
	struct rh_item *rh_item;
Packit 61cb5a
	struct rh_item_ci *ci;
Packit 61cb5a
	enum sf_cast_type cast_type;
Packit 61cb5a
	double avg_rtt;
Packit 61cb5a
	int i;
Packit 61cb5a
	int loss;
Packit 61cb5a
	int loss_adj;
Packit 61cb5a
	uint64_t received;
Packit 61cb5a
	uint64_t sent;
Packit 61cb5a
Packit 61cb5a
	printf("\n");
Packit 61cb5a
Packit 61cb5a
	loss_adj = 0;
Packit 61cb5a
Packit 61cb5a
	TAILQ_FOREACH(rh_item, remote_hosts, entries) {
Packit 61cb5a
		for (i = 0; i < 2; i++) {
Packit 61cb5a
			if (i == 0) {
Packit 61cb5a
				cast_type = SF_CT_UNI;
Packit 61cb5a
			} else {
Packit 61cb5a
				switch (transport_method) {
Packit 61cb5a
				case SF_TM_ASM:
Packit 61cb5a
				case SF_TM_SSM:
Packit 61cb5a
					cast_type = SF_CT_MULTI;
Packit 61cb5a
					break;
Packit 61cb5a
				case SF_TM_IPBC:
Packit 61cb5a
					cast_type = SF_CT_BROAD;
Packit 61cb5a
					break;
Packit 61cb5a
				default:
Packit 61cb5a
					DEBUG_PRINTF("Internal error - unknown transport method");
Packit 61cb5a
					errx(1, "Internal error - unknown transport method");
Packit 61cb5a
					/* NOTREACHED */
Packit 61cb5a
				}
Packit 61cb5a
			}
Packit 61cb5a
Packit 61cb5a
			cast_str = sf_cast_type_to_str(cast_type);
Packit 61cb5a
			ci = &rh_item->client_info;
Packit 61cb5a
Packit 61cb5a
			received = ci->no_received[i];
Packit 61cb5a
			sent = ci->no_sent;
Packit 61cb5a
Packit 61cb5a
			printf("%-*s : ", host_name_len, rh_item->addr->host_name);
Packit 61cb5a
Packit 61cb5a
			if (received == 0 && i == 0) {
Packit 61cb5a
				printf("response message never received\n");
Packit 61cb5a
				break;
Packit 61cb5a
			}
Packit 61cb5a
Packit 61cb5a
			if (i != 0) {
Packit 61cb5a
				loss_adj = get_packet_loss_percent(sent - ci->first_mcast_seq + 1,
Packit 61cb5a
				    received);
Packit 61cb5a
			}
Packit 61cb5a
Packit 61cb5a
			loss = get_packet_loss_percent(sent, received);
Packit 61cb5a
Packit 61cb5a
			if (received == 0) {
Packit 61cb5a
				avg_rtt = 0;
Packit 61cb5a
			} else {
Packit 61cb5a
				avg_rtt = ci->avg_rtt[i] / UTIL_NSINMS;
Packit 61cb5a
			}
Packit 61cb5a
Packit 61cb5a
			printf("%5scast, ", cast_str);
Packit 61cb5a
Packit 61cb5a
			printf("xmt/rcv/%%loss = ");
Packit 61cb5a
			printf("%"PRIu64"/%"PRIu64, sent, received);
Packit 61cb5a
Packit 61cb5a
			if (ci->no_dups[i] > 0) {
Packit 61cb5a
				printf("+%"PRIu64, ci->no_dups[i]);
Packit 61cb5a
			}
Packit 61cb5a
Packit 61cb5a
			printf("/%d%%", loss);
Packit 61cb5a
			if (i != 0 && ci->first_mcast_seq > 1) {
Packit 61cb5a
				printf(" (seq>=%"PRIu32" %d%%)", ci->first_mcast_seq, loss_adj);
Packit 61cb5a
			}
Packit 61cb5a
Packit 61cb5a
			printf(", min/avg/max/std-dev = ");
Packit 61cb5a
			printf("%.3f/%.3f/%.3f/%.3f", ci->rtt_min[i] / UTIL_NSINMS, avg_rtt,
Packit 61cb5a
			    ci->rtt_max[i] / UTIL_NSINMS,
Packit 61cb5a
			    util_ov_std_dev(ci->m2_rtt[i], ci->no_received[i]) / UTIL_NSINMS);
Packit 61cb5a
			printf("\n");
Packit 61cb5a
		}
Packit 61cb5a
	}
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Print packet statistics. host_name is remote host name with maximal host_name_len length. seq is
Packit 61cb5a
 * sequence number of packet, is_dup is boolean with information if packet is duplicate or not,
Packit 61cb5a
 * msg_len is length of message, dist_set is boolean variable with information if dist is set or
Packit 61cb5a
 * not. dist is distance of packet (how TTL was changed). rtt_set is boolean variable with
Packit 61cb5a
 * information if rtt (current round trip time) and avg_rtt (average round trip time) is set and
Packit 61cb5a
 * computed or not. loss is number of lost packets. cast_type is type of packet received
Packit 61cb5a
 * (unicast/multicast/broadcast). cont_stat is boolean variable saying, if to display
Packit 61cb5a
 * continuous statistic or not.
Packit 61cb5a
 */
Packit 61cb5a
static void
Packit 61cb5a
print_packet_stats(const char *host_name, int host_name_len, uint32_t seq, int is_dup,
Packit 61cb5a
    size_t msg_len, int dist_set, uint8_t dist, int rtt_set, double rtt, double avg_rtt, int loss,
Packit 61cb5a
    enum sf_cast_type cast_type, int cont_stat)
Packit 61cb5a
{
Packit 61cb5a
	const char *cast_str;
Packit 61cb5a
Packit 61cb5a
	cast_str = sf_cast_type_to_str(cast_type);
Packit 61cb5a
Packit 61cb5a
	printf("%-*s : ", host_name_len, host_name);
Packit 61cb5a
	printf("%5scast, ", cast_str);
Packit 61cb5a
	printf("seq=%"PRIu32, seq);
Packit 61cb5a
Packit 61cb5a
	if (is_dup) {
Packit 61cb5a
		printf(" (dup)");
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	printf(", ");
Packit 61cb5a
	printf("size=%zu bytes", msg_len);
Packit 61cb5a
Packit 61cb5a
	if (dist_set) {
Packit 61cb5a
		printf(", dist=%"PRIu8, dist);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (rtt_set) {
Packit 61cb5a
		printf(", time=%.3fms", rtt);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	if (cont_stat) {
Packit 61cb5a
		printf(" (");
Packit 61cb5a
Packit 61cb5a
		if (rtt_set) {
Packit 61cb5a
			printf("%.3f avg, ", avg_rtt);
Packit 61cb5a
		}
Packit 61cb5a
Packit 61cb5a
		printf("%d%% loss)", loss);
Packit 61cb5a
	}
Packit 61cb5a
Packit 61cb5a
	printf("\n");
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Register global signal handlers for application. sigaction is used to allow *BSD behavior, where
Packit 61cb5a
 * recvmsg, sendto, ... can return EINTR, what signal (Linux) doesn't do (functions are restarted
Packit 61cb5a
 * automatically)
Packit 61cb5a
 */
Packit 61cb5a
static void
Packit 61cb5a
register_signal_handlers(void)
Packit 61cb5a
{
Packit 61cb5a
	struct sigaction act;
Packit 61cb5a
Packit 61cb5a
	act.sa_handler = sigint_handler;
Packit 61cb5a
	sigemptyset(&act.sa_mask);
Packit 61cb5a
	act.sa_flags = 0;
Packit 61cb5a
Packit 61cb5a
	sigaction(SIGINT, &act, NULL);
Packit 61cb5a
Packit 61cb5a
	act.sa_handler = siginfo_handler;
Packit 61cb5a
#ifdef SIGINFO
Packit 61cb5a
	sigaction(SIGINFO, &act, NULL);
Packit 61cb5a
#endif
Packit 61cb5a
	sigaction(SIGUSR1, &act, NULL);
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Handler for SIGINFO signal
Packit 61cb5a
 */
Packit 61cb5a
static void
Packit 61cb5a
siginfo_handler(int sig)
Packit 61cb5a
{
Packit 61cb5a
	display_stats_requested++;
Packit 61cb5a
}
Packit 61cb5a
Packit 61cb5a
/*
Packit 61cb5a
 * Handler for SIGINT signal
Packit 61cb5a
 */
Packit 61cb5a
static void
Packit 61cb5a
sigint_handler(int sig)
Packit 61cb5a
{
Packit 61cb5a
	exit_requested++;
Packit 61cb5a
Packit 61cb5a
	DEBUG2_PRINTF("Exit requested %d times", exit_requested);
Packit 61cb5a
Packit 61cb5a
	if (exit_requested > MAX_EXIT_REQUESTS) {
Packit 61cb5a
		signal(SIGINT, SIG_DFL);
Packit 61cb5a
		kill(getpid(), SIGINT);
Packit 61cb5a
	}
Packit 61cb5a
}