<|start_filename|>lsm/Makefile<|end_filename|>
#
# (C) 2009-2011 <NAME> <<EMAIL>>
#
# License: GPLv2
#

VERSION	?= $(lastword $(shell grep ^Version: foolsm.spec))
PROGS	= foolsm
PKG     = foolsm

CC	= gcc
override CFLAGS	+= -w -O2 -DFOOLSM_VERSION=\"$(VERSION)\"
#override CFLAGS += -D NO_PLUGIN_EXPORT
#override CFLAGS += -D NO_PLUGIN_EXPORT_MUNIN
#override CFLAGS += -D NO_PLUGIN_EXPORT_STATUS

PREFIX ?= /usr/local
DESTDIR ?=
BINDIR ?= $(PREFIX)/sbin
ifeq ($(PREFIX), /usr)
ETCDIR ?= /etc/foolsm
else
ETCDIR ?= $(PREFIX)/etc/foolsm
endif
DOCDIR ?= $(PREFIX)/share/doc/foolsm
EXAMPLEDIR ?= $(DOCDIR)/examples
SCRIPTDIR ?= $(PREFIX)/libexec/foolsm

DOCFILES = README foolsm.conf.sample default_script.sample rsyslog-foolsm.conf.sample
SCRIPTS	= shorewall_script shorewall6_script default_script group_script

override CFLAGS += -D ETCDIR=\"$(ETCDIR)\"
override CFLAGS += -D SCRIPTDIR=\"$(SCRIPTDIR)\"

.PHONY:	all clean distclean tar rpm

all: $(PROGS)

foolsm: foolsm.o icmp_t.o icmp6_t.o config.o globals.o cksum.o forkexec.o signal_handler.o timecalc.o plugin_export.o save_statuses.o pidfile.o cmdline.o usage.o

clean distclean:
	rm -rf *~ .*~ *.o $(PROGS) debugfiles.list debuglinks.list debugsources.list *.orig

tar: distclean
	tar zcvf ../$(PKG)-$(VERSION).tar.gz \
		--transform=s,.,$(PKG)-$(VERSION), \
		--show-transformed-name \
		--exclude .git \
		--exclude .gitignore \
		.

rpm: tar
	cp ../$(PKG)-$(VERSION).tar.gz ~/rpmbuild/SOURCES
	cp $(PKG).spec ~/rpmbuild/SPECS
	rpmbuild -ba ~/rpmbuild/SPECS/$(PKG).spec

install: foolsm
	@mkdir -p ../blib/etc/balance/lsm
	@mkdir -p ../blib/bin
	@install -m u=rwx,go=x     foolsm ../blib/bin/
	@install -m u=rwx,go=r default_script balancer_event_script ../blib/etc/balance/lsm/


<|start_filename|>lsm/defs.h<|end_filename|>
/*

(C) 2009-2011 <NAME> <<EMAIL>>

License: GPLv2

*/

#ifndef __DEFS_H__
#define __DEFS_H__

#ifndef TRUE
#define TRUE	(1)
#define FALSE	(0)
#endif

#define MIN_PERHOST_INTERVAL (20000L) /* 20ms in between sends minimum */
#define DEFAULT_SELECT_WAIT  (10000L) /* wait at least 10ms for incoming packet */

#define FOLLOWED_PKTS (100) /* THIS ABSOLUTELY CAN'T EXCEED 0xffff (65535 decimal) OR THINGS BREAK */
#define SEQ_LIMITER   ((0x10000 / FOLLOWED_PKTS) * FOLLOWED_PKTS)

#define min(x, y) ((x)<(y) ? (x) : (y))

#define PLUGIN_EXPORT_DIR "/var/lib/foolsm"

#endif

/* EOF */


<|start_filename|>lsm/usage.h<|end_filename|>
/*

  (C) 2014 <NAME> <<EMAIL>>

  License: GPLv2

*/

#ifndef __USAGE_H__
#define __USAGE_H__

void usage_and_exit(void);

#endif

/* EOF */


<|start_filename|>lsm/plugin_export.c<|end_filename|>
/*

(C) 2013 <NAME> <<EMAIL>>

License: GPLv2

*/

#ifndef NO_PLUGIN_EXPORT

#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <sys/time.h>
#include <string.h>
#include <ctype.h>

#include "plugin_export.h"
#include "timecalc.h"
#include "defs.h"
#ifndef NO_PLUGIN_EXPORT_STATUS
#include "globals.h"
#endif

#ifndef NO_PLUGIN_EXPORT_MUNIN
static char *munin_data_src_name(const char *src);
static void plugin_export_munin(CONFIG *first);
#endif

static struct timeval export_time = {0, 0};

void plugin_export_init(void)
{
	gettimeofday(&export_time, NULL);
}

void plugin_export(CONFIG *first)
{
	struct timeval current_time = {0, 0};

	gettimeofday(&current_time, NULL);

	/* export every 300s */
	if(timeval_diff_cmp(&current_time, &export_time, TIMEVAL_DIFF_CMP_GT, 300, 0) == FALSE) return;

	/* next export after 300 sec */
	timeval_add(&export_time, 300, 0);

#ifndef NO_PLUGIN_EXPORT_MUNIN
	plugin_export_munin(first);
#endif
}

#ifndef NO_PLUGIN_EXPORT_MUNIN
static void plugin_export_munin(CONFIG *first)
{
	FILE *fp;
	char buf[BUFSIZ];
	CONFIG *cur;
	TARGET *t;

	/* export avg_rtt graph config */
	snprintf(buf, BUFSIZ - 1, "%s/%s", PLUGIN_EXPORT_DIR, "config.rtt");

	if((fp = fopen(buf, "w")) == NULL) {
		syslog(LOG_ERR, "%s: %s: failed to open file %s for write", __FILE__, __FUNCTION__, buf);
		return;
	}

	fprintf(fp, "graph_title Foolsm Average Ping Latency\n");
	fprintf(fp, "graph_vlabel ms\n");
	fprintf(fp, "graph_info This graph shows Foolsm status\n");
	fprintf(fp, "graph_category network\n");
	fprintf(fp, "graph_args --base 1000 -l 0\n");

	for(cur = first; cur; cur = cur->next) {
		char *name = munin_data_src_name(cur->name);

		fprintf(fp, "%s_rtt.label %s rtt\n", name, cur->name);
		fprintf(fp, "%s_rtt.type GAUGE\n", name);
	}

	fclose(fp);

	/* export avg_rtt values */
	snprintf(buf, BUFSIZ - 1, "%s/%s", PLUGIN_EXPORT_DIR, "status.rtt");

	if((fp = fopen(buf, "w")) == NULL) {
		syslog(LOG_ERR, "%s: %s: failed to open file %s for write", __FILE__, __FUNCTION__, buf);
		return;
	}

	for(cur = first; cur; cur = cur->next) {
		char *name = munin_data_src_name(cur->name);
		t = cur->data;

		fprintf(fp, "%s_rtt.value %.2f\n", name, (t->status == DOWN || t->status == LONG_DOWN) ? 0.0 : t->avg_rtt / 1000.0);
	}

	fclose(fp);

	/* export other counts config */
	snprintf(buf, BUFSIZ - 1, "%s/%s", PLUGIN_EXPORT_DIR, "config.counts");

	if((fp = fopen(buf, "w")) == NULL) {
		syslog(LOG_ERR, "%s: %s: failed to open file %s for write", __FILE__, __FUNCTION__, buf);
		return;
	}

	fprintf(fp, "graph_title Foolsm packet counts\n");
	fprintf(fp, "graph_vlabel percent\n");
	fprintf(fp, "graph_info This graph shows Foolsm status\n");
	fprintf(fp, "graph_category network\n");
	fprintf(fp, "graph_args --base 1000 -l 0\n");

	for(cur = first; cur; cur = cur->next) {
		char *name = munin_data_src_name(cur->name);

		fprintf(fp, "%s_timeout.label %s Timed out\n", name, cur->name);
		fprintf(fp, "%s_timeout.type GAUGE\n", name);

		fprintf(fp, "%s_replied.label %s Replied\n", name, cur->name);
		fprintf(fp, "%s_replied.type GAUGE\n", name);

		fprintf(fp, "%s_waiting.label %s Waiting\n", name, cur->name);
		fprintf(fp, "%s_waiting.type GAUGE\n", name);

		fprintf(fp, "%s_latereply.label %s Late replied\n", name, cur->name);
		fprintf(fp, "%s_latereply.type GAUGE\n", name);

		fprintf(fp, "%s_cwait.label %s Consecutive waiting\n", name, cur->name);
		fprintf(fp, "%s_cwait.type GAUGE\n", name);

		fprintf(fp, "%s_cmiss.label %s Consecutive missing\n", name, cur->name);
		fprintf(fp, "%s_cmiss.type GAUGE\n", name);

		fprintf(fp, "%s_crcvd.label %s Consecutive received\n", name, cur->name);
		fprintf(fp, "%s_crcvd.type GAUGE\n", name);
	}

	fclose(fp);

	/* export other counts values */
	snprintf(buf, BUFSIZ - 1, "%s/%s", PLUGIN_EXPORT_DIR, "status.counts");

	if((fp = fopen(buf, "w")) == NULL) {
		syslog(LOG_ERR, "%s: %s: failed to open file %s for write", __FILE__, __FUNCTION__, buf);
		return;
	}

	for(cur = first; cur; cur = cur->next) {
		char *name = munin_data_src_name(cur->name);
		t = cur->data;

		fprintf(fp, "%s_timeout.value %d\n", name, t->timeout);

		fprintf(fp, "%s_replied.value %d\n", name, t->replied);

		fprintf(fp, "%s_waiting.value %d\n", name, t->waiting);

		fprintf(fp, "%s_latereply.value %d\n", name, t->reply_late);

		fprintf(fp, "%s_cwait.value %d\n", name, t->consecutive_waiting);

		fprintf(fp, "%s_cmiss.value %d\n", name, t->consecutive_missing);

		fprintf(fp, "%s_crcvd.value %d\n", name, t->consecutive_rcvd);
	}

	fclose(fp);

	/* export connection status config */
	snprintf(buf, BUFSIZ - 1, "%s/%s", PLUGIN_EXPORT_DIR, "config.status");

	if((fp = fopen(buf, "w")) == NULL) {
		syslog(LOG_ERR, "%s: %s: failed to open file %s for write", __FILE__, __FUNCTION__, buf);
		return;
	}

	fprintf(fp, "graph_title Foolsm connection statuses\n");
	fprintf(fp, "graph_vlabel Status\n");
	fprintf(fp, "graph_info This graph shows Foolsm connection statuses\n");
	fprintf(fp, "graph_category network\n");
	fprintf(fp, "graph_info Status: 0 = DOWN, 1 = UP, 2 = UNKNOWN, 3 = LONG_DOWN\n");
	fprintf(fp, "graph_args --base 1000 --lower-limit 0 --upper-limit 3\n");

	for(cur = first; cur; cur = cur->next) {
		char *name = munin_data_src_name(cur->name);

		fprintf(fp, "%s_status.label %s Status\n", name, cur->name);
	}

	fclose(fp);

	/* export connection status values */
	snprintf(buf, BUFSIZ - 1, "%s/%s", PLUGIN_EXPORT_DIR, "status.status");

	if((fp = fopen(buf, "w")) == NULL) {
		syslog(LOG_ERR, "%s: %s: failed to open file %s for write", __FILE__, __FUNCTION__, buf);
		return;
	}

	for(cur = first; cur; cur = cur->next) {
		char *name = munin_data_src_name(cur->name);
		t = cur->data;

		fprintf(fp, "%s_status.value %d\n", name, t->status);
	}

	fclose(fp);
}
#endif

#ifndef NO_PLUGIN_EXPORT_STATUS
void plugin_export_status(CONFIG *first)
{
	FILE *fp;
	char buf[BUFSIZ];
	CONFIG *cur;
	TARGET *t;

	snprintf(buf, BUFSIZ - 1, "%s/%s", PLUGIN_EXPORT_DIR, "status_export");

	if((fp = fopen(buf, "w")) == NULL) {
		syslog(LOG_ERR, "%s: %s: failed to open file %s for write", __FILE__, __FUNCTION__, buf);
		return;
	}

	for(cur = first; cur; cur = cur->next) {
		t = cur->data;

		fprintf(fp, "%s %s\n", cur->name, get_status_str(t->status));
	}

	fclose(fp);
}
#endif

#ifndef NO_PLUGIN_EXPORT_MUNIN
static char *munin_data_src_name(const char *src)
{
	static char buf[BUFSIZ];
	char *p;

	strcpy(buf, "_");

	strncat(buf, src, BUFSIZ - 1);

	for(p = buf; *p; p++) {
		if(*p == '-') *p = '_';
		if(*p == ' ') *p = '_';
	}
	return(buf);
}
#endif

#endif

/* EOF */


<|start_filename|>lsm/foolsm.c<|end_filename|>
/*

(C) 2009-2019 <NAME> <<EMAIL>>

License: GPLv2

*/

#define _GNU_SOURCE

#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <netinet/in_systm.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/select.h>
#include <syslog.h>
#include <sys/wait.h>
#include <stdarg.h>
#include <fcntl.h>

#include <sys/ioctl.h>
#include <linux/if.h>
#include <sys/uio.h>

#include "icmp_t.h"
#include "icmp6_t.h"
#include "config.h"
#include "cksum.h"
#include "globals.h"
#include "signal_handler.h"
#include "forkexec.h"
#include "timecalc.h"
#include "foolsm.h"
#ifndef NO_PLUGIN_EXPORT
#include "plugin_export.h"
#endif
#include "save_statuses.h"
#include "pidfile.h"
#include "cmdline.h"
#include "usage.h"

typedef struct ping_data {
	unsigned short id;       /* target id */
	long ping_count;         /* counts up to -c count or 1 */
	struct timeval ping_ts;  /* time sent */
} PING_DATA;

static void update_stats(CONFIG *first);
static void dump_statuses(CONFIG *first);
static void decide(CONFIG *first);
static void groups_decide(GROUPS *firstg);
static int wait_for_replies(CONFIG **ctable);
static int ping_send(CONFIG *cur);
static int ping_rcv(CONFIG *first, char *buf, int len, struct sockaddr_in6 *saddr, unsigned int *slen, long usec, CONFIG **arp);
static int event_script_check(const char *path);
static int open_arp_sock(CONFIG *cur);
static int open_icmp_sock(CONFIG *cur);
static int probe_src_ip_addr(CONFIG *cur);
static void init_config_data(CONFIG *first, CONFIG *last, CONFIG ***ctable);
static void free_config_data(CONFIG *first);
#if defined(DEBUG)
static void dump_pkt(const void *buf, size_t len);
#endif

static int num_hosts = 0;

/* Main */
int main(int argc, char *argv[]) {
	TARGET *t = NULL;
	CONFIG *first = NULL, *last = NULL, *cur;
	GROUPS *firstg = NULL, *lastg = NULL;
	CONFIG **ctable = NULL;
	struct timeval last_sent_time = {0, 0};
	int start = 0;

	openlog("foolsm", LOG_PID, LOG_DAEMON);

	cmdline_parse(argc, argv);

	init_config();

	if(read_config(get_configfile(), &first, &last, &firstg, &lastg)) {
		usage_and_exit();
	}

	if(cfg.debug >= 9) syslog(LOG_INFO, "my ident is %d\n", get_ident());

	if(!first) {
		syslog(LOG_ERR, "no targets found in config file");
		exit(1);
	}

	if(cfg.debug >= 9) dump_config(&first, &last, &firstg, &lastg);

	/* check pid file */
	if(pidfile_open() != 0) exit(1);

	/* detach from controlling terminal if nodaemon global is not set */
	if(get_nodaemon() == 0) {
		if(daemon(1, 0)) {
			syslog(LOG_ERR, "daemon failed while trying to detach");
			return(1);
		}
	}

	if(pidfile_update() != 0) exit(1);

	set_ident(getpid() & 0xFFFF);

#ifndef NO_PLUGIN_EXPORT
	plugin_export_init();
#endif

	init_config_data(first, last, &ctable);

	signal(SIGINT, signal_handler);
	signal(SIGUSR1, signal_handler);
	signal(SIGUSR2, signal_handler);
	signal(SIGHUP, signal_handler);

	/*
	  Create the handler for child signals. This will clean up
	  any forked child after an event has occured.
	*/
	create_sigchld_hdl();

	struct timeval last_decision = {0, 0};

	/* the main loop */
	while(get_cont()) {
		struct timeval tv = {0, 0};

		if(get_reload_cfg()) {

			save_statuses(first);

			/* reload config */
			free(ctable);
			free_config_data(first);
			if(reload_config(get_configfile(), &first, &last, &firstg, &lastg)) {
				syslog(LOG_ERR, "reload config failed");
				exit(2);
			}
			init_config_data(first, last, &ctable);

			restore_statuses(first);

			set_reload_cfg(0);
		}

		for(cur = first; cur; cur = cur->next) {
			struct timeval current_time = {0, 0};

			if(start)
				while(wait_for_replies(ctable));

			if(gettimeofday(&current_time, NULL) == -1) {
				syslog(LOG_INFO, "gettimeofday failed \"%s\"", strerror(errno));
				sleep(1);
				continue;
			}

			t = cur->data;

			if(timeval_diff_cmp(&current_time, &last_sent_time, TIMEVAL_DIFF_CMP_LT, MIN_PERHOST_INTERVAL / 1000000L, MIN_PERHOST_INTERVAL % 1000000L)) continue;

			if(cur->startup_burst_pkts && t->used <= cur->startup_burst_pkts && !timeval_diff_cmp(&current_time, &(t->last_send_time), TIMEVAL_DIFF_CMP_LT, (cur->startup_burst_interval * 1000) / 1000000L, (cur->startup_burst_interval * 1000) % 1000000L));
			else if(timeval_diff_cmp(&current_time, &(t->last_send_time), TIMEVAL_DIFF_CMP_LT, (cur->interval_ms * 1000) / 1000000L, (cur->interval_ms * 1000) % 1000000L)) continue;

			if(cur->check_arp) {
				open_arp_sock(cur);
			} else {
				open_icmp_sock(cur);
			}

			if(ping_send(cur)) {
				if(cfg.debug >= 9) syslog(LOG_INFO, "ping_send failed to %s", cur->name);
			}
			else {
				gettimeofday(&last_sent_time, NULL);
				start = 1;
			}
		}

		gettimeofday(&tv, NULL);
		if(timeval_diff_cmp(&tv, &last_decision, TIMEVAL_DIFF_CMP_GT, 1, 0)) { /* make decisions at 1s intervals */
			gettimeofday(&last_decision, NULL);

			update_stats(first);
			decide(first);
			dump_statuses(first);

			groups_decide(firstg);

#if defined(DEBUG)
			exec_queue_dump();
#endif
			exec_queue_process();

#ifndef NO_PLUGIN_EXPORT
			plugin_export(first);
#endif
		}
	} /* while cont */

	/* if we wrote pid file then close and remove it */
	pidfile_close();

	free(ctable);
	free_config_data(first);
	free_config(&first, &last, &firstg, &lastg);
	exec_queue_free();

	closelog();

	return(0);
}

static void free_config_data(CONFIG *first) {
	CONFIG *cur;

	for(cur = first; cur; cur = cur->next) {
		TARGET *t;

		t = cur->data;
		if(t->sock != -1) close(t->sock);
		free(t);
	}
}

static void update_stats(CONFIG *first) {
	struct timeval current_time = {0, 0};
	CONFIG *cur;

	gettimeofday(&current_time, NULL);

	for(cur = first; cur; cur = cur->next) {
		TARGET *t;
		int i, seq, ind;
		long rtt = 0;

		t = cur->data;

		t->timeout = 0;
		t->replied = 0;
		t->waiting = 0;
		t->reply_late = 0;
		t->consecutive_waiting = 0;
		t->consecutive_missing = 0;
		t->consecutive_rcvd = 0;

		/* check consecutive pkts */
		seq = t->seq % FOLLOWED_PKTS;

		for(i = (seq - 2); i > (seq - 2) - FOLLOWED_PKTS; i--) {
			ind = (i >= 0) ? i : i + FOLLOWED_PKTS;
			if(!t->sentpkts[ind].flags.used) break;

			if(t->sentpkts[ind].flags.waiting) t->consecutive_waiting++;
			else break;
		}

		for(i = (seq - 2); i > (seq - 2) - FOLLOWED_PKTS; i--) {
			ind = (i >= 0) ? i : i + FOLLOWED_PKTS;
			if(!t->sentpkts[ind].flags.used) break;

			if(t->sentpkts[ind].flags.timeout || t->sentpkts[ind].flags.waiting) t->consecutive_missing++;
			else break;
		}

		for(i = (seq - 2); i > (seq - 2) - FOLLOWED_PKTS; i--) {
			ind = (i >= 0) ? i : i + FOLLOWED_PKTS;
			if(!t->sentpkts[ind].flags.used) break;

			if(t->sentpkts[ind].flags.replied && !t->sentpkts[ind].flags.timeout) t->consecutive_rcvd++;
			else break;
		}

		/* count pkt states */
		for(i = 0; i < FOLLOWED_PKTS; i++) {
			if(!t->sentpkts[i].flags.used) continue;

			if(timeval_diff_cmp(&current_time, &t->sentpkts[i].sent_time, TIMEVAL_DIFF_CMP_GT, (cur->timeout_ms * 1000) / 1000000L, (cur->timeout_ms * 1000) % 1000000L) && t->sentpkts[i].flags.waiting) {
				t->sentpkts[i].flags.timeout = 1;
			}

			if(t->sentpkts[i].flags.replied && t->sentpkts[i].flags.timeout) t->reply_late++;

			if(t->sentpkts[i].flags.replied) {
				t->replied++;
				rtt += t->sentpkts[i].rtt; /* count rtt sum in usec from replied pkts rtt which is in usec */
			}
			if(t->sentpkts[i].flags.timeout) t->timeout++;
			if(t->sentpkts[i].flags.waiting) t->waiting++;
		}
		/* avg_rtt in usec */
		t->avg_rtt = rtt / (t->replied ? t->replied : 1);

		/* update loss max info */
		if(t->timeout > t->timeout_max) t->timeout_max = t->timeout;
		if(t->consecutive_missing > t->consecutive_missing_max) t->consecutive_missing_max = t->consecutive_missing;

		if(cfg.debug >= 9) syslog(LOG_INFO, "name = %s, replied = %d, waiting = %d, timeout = %d, late reply = %d, cons rcvd = %d, cons wait = %d, cons miss = %d, avg_rtt = %.3f, seq = %d, status = %s",
					  cur->name, t->replied, t->waiting, t->timeout, t->reply_late, t->consecutive_rcvd, t->consecutive_waiting, t->consecutive_missing, t->avg_rtt / 1000.0, t->seq, get_status_str(t->status));

	}
}

static void dump_statuses(CONFIG *first) {
	CONFIG *cur;

	for(cur = first; cur; cur = cur->next) {
		TARGET *t;

		t = cur->data;

		if((t->status == DOWN || t->status == LONG_DOWN) && t->downseq == (t->seq % FOLLOWED_PKTS) && t->seq != t->downseqreported && !t->status_change) syslog(LOG_INFO, "link %s still down", cur->name);

		/* dump is controlled by SIGUSR1 and then we should show all statuses anyway */
		if(get_dump() || t->status_change || ((t->status == DOWN || t->status == LONG_DOWN) && t->downseq == (t->seq % FOLLOWED_PKTS) && t->seq != t->downseqreported && !t->status_change)) {
			if(cfg.debug >= 6) syslog(LOG_INFO, "name = %s, replied = %d, waiting = %d, timeout = %d, timeout max = %d, late reply = %d, cons rcvd = %d, cons wait = %d, cons miss = %d, cons miss max = %d, avg_rtt = %.3f, seq = %d, status = %s",
						  cur->name, t->replied, t->waiting, t->timeout, t->timeout_max, t->reply_late, t->consecutive_rcvd, t->consecutive_waiting, t->consecutive_missing, t->consecutive_missing_max, t->avg_rtt / 1000.0, t->seq, get_status_str(t->status));

			if(cfg.debug >= 7) {
				/* 100 should be enough for the comments and such, but I don't care to count */
				char buf[FOLLOWED_PKTS + 100];
				int i, seq;

				seq = t->seq % FOLLOWED_PKTS;

				sprintf(buf, "seq        ");
				for(i = 0; i < FOLLOWED_PKTS; i++) {
					if(i == seq) strcat(buf, "*");
					else strcat(buf, " ");
				}
				syslog(LOG_INFO, "%s", buf);

				sprintf(buf, "used       ");
				for(i = 0; i < FOLLOWED_PKTS; i++) {
					sprintf(buf + strlen(buf), "%d", t->sentpkts[i].flags.used);
				}
				syslog(LOG_INFO, "%s", buf);

				sprintf(buf, "wait       ");
				for(i = 0; i < FOLLOWED_PKTS; i++) {
					sprintf(buf + strlen(buf), "%d", t->sentpkts[i].flags.waiting);
				}
				syslog(LOG_INFO, "%s", buf);

				sprintf(buf, "replied    ");
				for(i = 0; i < FOLLOWED_PKTS; i++) {
					sprintf(buf + strlen(buf), "%d", t->sentpkts[i].flags.replied);
				}
				syslog(LOG_INFO, "%s", buf);

				sprintf(buf, "timeout    ");
				for(i = 0; i < FOLLOWED_PKTS; i++) {
					sprintf(buf + strlen(buf), "%d", t->sentpkts[i].flags.timeout);
				}
				syslog(LOG_INFO, "%s", buf);

				sprintf(buf, "error      ");
				for(i = 0; i < FOLLOWED_PKTS; i++) {
					sprintf(buf + strlen(buf), "%d", t->sentpkts[i].flags.error);
				}
				syslog(LOG_INFO, "%s", buf);

				if(t->status == UP && t->status_change) {
					t->timeout_max = 0;
					t->consecutive_missing_max = 0;
				}
			}

			t->downseqreported = t->seq;
		}
	}
	if(get_dump()) set_dump(0); /* if we just dumped then don't dump next time. flags don't change that frequently */
}

static void decide(CONFIG *first) {
	struct timeval current_time = {0, 0};
	CONFIG *cur;

	gettimeofday(&current_time, NULL);

	for(cur = first; cur; cur = cur->next) {
		TARGET *t;
		STATUS prevstatus;

		t = cur->data;

		/* reset any previous connection status_change state */
		t->status_change = 0;

		prevstatus = t->status;

		/* up or unknown */
		if(t->status == UP || t->status == UNKNOWN) {
			if(t->timeout >= cur->max_packet_loss || t->consecutive_missing >= cur->max_successive_pkts_lost) {
				/* change to down state */
				t->status_change = 1;
				t->status = DOWN;

#if !defined(NO_PLUGIN_EXPORT) && !defined(NO_PLUGIN_EXPORT_STATUS)
				plugin_export_status(first);
#endif

				if(cfg.debug >= 8) syslog(LOG_INFO, "link %s down event", cur->name);
				if(event_script_check(cur->eventscript)) {
					char sbuf[INET6_ADDRSTRLEN];
					char **argv;
					char **envp;

					argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d",
							       cur->eventscript,
							       get_status_str(t->status),
							       cur->name,
							       cur->checkip,
							       cur->device ? cur->device : "",
							       cur->warn_email ? cur->warn_email : "",
							       t->replied,
							       t->waiting,
							       t->timeout,
							       t->reply_late,
							       t->consecutive_rcvd,
							       t->consecutive_waiting,
							       t->consecutive_missing,
							       t->avg_rtt,
							       cur->dstinfo->ai_family == AF_INET ? inet_ntoa(t->src) : inet_ntop(AF_INET6, &t->src6, sbuf, INET6_ADDRSTRLEN),
							       get_status_str(prevstatus),
							       current_time.tv_sec);
					envp = exec_queue_envp();

					if(cur->queue && *cur->queue) {
						exec_queue_add(cur->queue, argv, envp);
					} else {
						forkexec(argv, envp);

						exec_queue_argv_free(argv);
						exec_queue_envp_free(envp);
					}
				}

				if(event_script_check(cur->notifyscript)) {
					char sbuf[INET6_ADDRSTRLEN];
					char **argv;
					char **envp;

					argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d",
							       cur->notifyscript,
							       get_status_str(t->status),
							       cur->name,
							       cur->checkip,
							       cur->device ? cur->device : "",
							       cur->warn_email ? cur->warn_email : "",
							       t->replied,
							       t->waiting,
							       t->timeout,
							       t->reply_late,
							       t->consecutive_rcvd,
							       t->consecutive_waiting,
							       t->consecutive_missing,
							       t->avg_rtt,
							       cur->dstinfo->ai_family == AF_INET ? inet_ntoa(t->src) : inet_ntop(AF_INET6, &t->src6, sbuf, INET6_ADDRSTRLEN),
							       get_status_str(prevstatus),
							       current_time.tv_sec);

					envp = exec_queue_envp();

					forkexec(argv, envp);

					exec_queue_argv_free(argv);
					exec_queue_envp_free(envp);
				}

				if(gettimeofday(&t->down_timestamp, NULL) == -1) {
					syslog(LOG_INFO, "gettimeofday failed \"%s\"", strerror(errno));
				}
				t->downseq = t->seq % FOLLOWED_PKTS;
				t->downseqreported = 0;
			}
		}

		/* has it been down long? */
		if(t->status == DOWN && cur->long_down_time) {
			if(timeval_diff_cmp(&current_time, &t->down_timestamp, TIMEVAL_DIFF_CMP_GT, cur->long_down_time, 0)) {
				/* special, LONG_DOWN is considered DOWN thus no status_change */
				t->status = LONG_DOWN;

				if(cfg.debug >= 8) syslog(LOG_INFO, "link %s long down event", cur->name);
				if(event_script_check(cur->long_down_eventscript)) {
					char sbuf[INET6_ADDRSTRLEN];
					char **argv;
					char **envp;

					argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d",
							       cur->long_down_eventscript,
							       get_status_str(t->status),
							       cur->name,
							       cur->checkip,
							       cur->device ? cur->device : "",
							       cur->long_down_email ? cur->long_down_email : "",
							       t->replied,
							       t->waiting,
							       t->timeout,
							       t->reply_late,
							       t->consecutive_rcvd,
							       t->consecutive_waiting,
							       t->consecutive_missing,
							       t->avg_rtt,
							       cur->dstinfo->ai_family == AF_INET ? inet_ntoa(t->src) : inet_ntop(AF_INET6, &t->src6, sbuf, INET6_ADDRSTRLEN),
							       get_status_str(prevstatus),
							       t->down_timestamp.tv_sec);

					envp = exec_queue_envp();

					if(cur->queue && *cur->queue) {
						exec_queue_add(cur->queue, argv, envp);
					} else {
						forkexec(argv, envp);

						exec_queue_argv_free(argv);
						exec_queue_envp_free(envp);
					}
				}

				if(event_script_check(cur->long_down_notifyscript)) {
					char sbuf[INET6_ADDRSTRLEN];
					char **argv;
					char **envp;

					argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d",
							       cur->long_down_notifyscript,
							       get_status_str(t->status),
							       cur->name,
							       cur->checkip,
							       cur->device ? cur->device : "",
							       cur->long_down_email ? cur->long_down_email : "",
							       t->replied,
							       t->waiting,
							       t->timeout,
							       t->reply_late,
							       t->consecutive_rcvd,
							       t->consecutive_waiting,
							       t->consecutive_missing,
							       t->avg_rtt,
							       cur->dstinfo->ai_family == AF_INET ? inet_ntoa(t->src) : inet_ntop(AF_INET6, &t->src6, sbuf, INET6_ADDRSTRLEN),
							       get_status_str(prevstatus),
							       t->down_timestamp.tv_sec);

					envp = exec_queue_envp();

					forkexec(argv, envp);

					exec_queue_argv_free(argv);
					exec_queue_envp_free(envp);
				}
			}
		}

		/* down, long down or unknown */
		if(t->status == DOWN || t->status == LONG_DOWN || t->status == UNKNOWN) {
			if((cur->startup_acceleration && t->consecutive_rcvd >= cur->startup_acceleration && (t->consecutive_rcvd + 1) >= t->used) || (t->timeout <= cur->min_packet_loss && t->consecutive_rcvd >= cur->min_successive_pkts_rcvd)) {

				t->status_change = 1;
				t->status = UP;

#if !defined(NO_PLUGIN_EXPORT) && !defined(NO_PLUGIN_EXPORT_STATUS)
				plugin_export_status(first);
#endif

				/* report long_down to up */
				if(prevstatus == LONG_DOWN) {
					if(event_script_check(cur->long_down_eventscript)) {
						char sbuf[INET6_ADDRSTRLEN];
						char **argv;
						char **envp;

						argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d",
								       cur->long_down_eventscript,
								       get_status_str(t->status),
								       cur->name,
								       cur->checkip,
								       cur->device ? cur->device : "",
								       cur->long_down_email ? cur->long_down_email : "",
								       t->replied,
								       t->waiting,
								       t->timeout,
								       t->reply_late,
								       t->consecutive_rcvd,
								       t->consecutive_waiting,
								       t->consecutive_missing,
								       t->avg_rtt,
								       cur->dstinfo->ai_family == AF_INET ? inet_ntoa(t->src) : inet_ntop(AF_INET6, &t->src6, sbuf, INET6_ADDRSTRLEN),
								       get_status_str(prevstatus),
								       current_time.tv_sec);

						envp = exec_queue_envp();

						if(cur->queue && *cur->queue) {
							exec_queue_add(cur->queue, argv, envp);
						} else {
							forkexec(argv, envp);

							exec_queue_argv_free(argv);
							exec_queue_envp_free(envp);
						}
					}

					if(event_script_check(cur->long_down_notifyscript)) {
						char sbuf[INET6_ADDRSTRLEN];
						char **argv;
						char **envp;

						argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d",
								       cur->long_down_notifyscript,
								       get_status_str(t->status),
								       cur->name,
								       cur->checkip,
								       cur->device ? cur->device : "",
								       cur->long_down_email ? cur->long_down_email : "",
								       t->replied,
								       t->waiting,
								       t->timeout,
								       t->reply_late,
								       t->consecutive_rcvd,
								       t->consecutive_waiting,
								       t->consecutive_missing,
								       t->avg_rtt,
								       cur->dstinfo->ai_family == AF_INET ? inet_ntoa(t->src) : inet_ntop(AF_INET6, &t->src6, sbuf, INET6_ADDRSTRLEN),
								       get_status_str(prevstatus),
								       current_time.tv_sec);

						envp = exec_queue_envp();

						forkexec(argv, envp);

						exec_queue_argv_free(argv);
						exec_queue_envp_free(envp);
					}
				}

				/* change to up state */
				if(cfg.debug >= 8) syslog(LOG_INFO, "link %s up event", cur->name);
				if(event_script_check(cur->eventscript)) {
					char sbuf[INET6_ADDRSTRLEN];
					char **argv;
					char **envp;

					argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d",
							       cur->eventscript,
							       get_status_str(t->status),
							       cur->name,
							       cur->checkip,
							       cur->device ? cur->device : "",
							       cur->warn_email ? cur->warn_email : "",
							       t->replied,
							       t->waiting,
							       t->timeout,
							       t->reply_late,
							       t->consecutive_rcvd,
							       t->consecutive_waiting,
							       t->consecutive_missing,
							       t->avg_rtt,
							       cur->dstinfo->ai_family == AF_INET ? inet_ntoa(t->src) : inet_ntop(AF_INET6, &t->src6, sbuf, INET6_ADDRSTRLEN),
							       get_status_str(prevstatus),
							       current_time.tv_sec);

					envp = exec_queue_envp();

					if(cur->queue && *cur->queue) {
						exec_queue_add(cur->queue, argv, envp);
					} else {
						forkexec(argv, envp);

						exec_queue_argv_free(argv);
						exec_queue_envp_free(envp);
					}
				}

				if((cur->unknown_up_notify || t->status != UNKNOWN) && event_script_check(cur->notifyscript)) {
					char sbuf[INET6_ADDRSTRLEN];
					char **argv;
					char **envp;

					argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d",
							       cur->notifyscript,
							       get_status_str(t->status),
							       cur->name,
							       cur->checkip,
							       cur->device ? cur->device : "",
							       cur->warn_email ? cur->warn_email : "",
							       t->replied,
							       t->waiting,
							       t->timeout,
							       t->reply_late,
							       t->consecutive_rcvd,
							       t->consecutive_waiting,
							       t->consecutive_missing,
							       t->avg_rtt,
							       cur->dstinfo->ai_family == AF_INET ? inet_ntoa(t->src) : inet_ntop(AF_INET6, &t->src6, sbuf, INET6_ADDRSTRLEN),
							       get_status_str(prevstatus),
							       current_time.tv_sec);

					envp = exec_queue_envp();

					forkexec(argv, envp);

					exec_queue_argv_free(argv);
					exec_queue_envp_free(envp);
				}
			}
		}
	}
}

static void groups_decide(GROUPS *firstg){
	GROUPS *curg;
	GROUP_MEMBERS *curgm;
	TARGET *t;
	STATUS prevstatus;
	struct timeval current_time = {0, 0};

	gettimeofday(&current_time, NULL);

	curg = firstg;
	while(curg) {
		prevstatus = curg->status;
		curg->status = curg->logic;

		curgm = curg->fgm;
		while(curgm) {
			if(!curgm->cfg_ptr) break;

			t = curgm->cfg_ptr->data;

			/* if any one group member is in unknown status, group is in unknown status */
			if(t->status == UNKNOWN) {
				curg->status = UNKNOWN;
				break;
			}

			if(!curg->logic) {
				curg->status |= (t->status == DOWN || t->status == LONG_DOWN) ? DOWN : t->status;
			} else {
				curg->status &= (t->status == DOWN || t->status == LONG_DOWN) ? DOWN : t->status;
			}

			curgm = curgm->next;
		}
		if(curg->status != prevstatus) {
			if(curg->status == UP) {
				/* group up event */
				if(cfg.debug >= 8) syslog(LOG_INFO, "group %s up event", curg->name);
				if(event_script_check(curg->eventscript)) {
					char **argv;
					char **envp;

					argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d",
							       curg->eventscript,
							       get_status_str(curg->status),
							       curg->name,
							       "",
							       curg->device ? curg->device : "",
							       curg->warn_email ? curg->warn_email : "",
							       0,
							       0,
							       0,
							       0,
							       0,
							       0,
							       0,
							       0,
							       "",
							       get_status_str(prevstatus),
							       current_time.tv_sec);
					envp = exec_queue_envp();

					if(curg->queue && *curg->queue) {
						exec_queue_add(curg->queue, argv, envp);
					} else {
						forkexec(argv, envp);

						exec_queue_argv_free(argv);
						exec_queue_envp_free(envp);
					}
				}
				if((curg->unknown_up_notify || prevstatus != UNKNOWN) && event_script_check(curg->notifyscript)) {
					char **argv;
					char **envp;

					argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d",
							       curg->notifyscript,
							       get_status_str(curg->status),
							       curg->name,
							       "",
							       curg->device ? curg->device : "",
							       curg->warn_email ? curg->warn_email : "",
							       0,
							       0,
							       0,
							       0,
							       0,
							       0,
							       0,
							       0,
							       "",
							       get_status_str(prevstatus),
							       current_time.tv_sec);
					envp = exec_queue_envp();

					forkexec(argv, envp);

					exec_queue_argv_free(argv);
					exec_queue_envp_free(envp);
				}
			}

			if(curg->status == DOWN) {
				/* group down event */
				if(cfg.debug >= 8) syslog(LOG_INFO, "group %s down event", curg->name);
				if(event_script_check(curg->eventscript)) {
					char **argv;
					char **envp;

					argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d",
							       curg->eventscript,
							       get_status_str(curg->status),
							       curg->name,
							       "",
							       curg->device ? curg->device : "",
							       curg->warn_email ? curg->warn_email : "",
							       0,
							       0,
							       0,
							       0,
							       0,
							       0,
							       0,
							       0,
							       "",
							       get_status_str(prevstatus),
							       current_time.tv_sec);
					envp = exec_queue_envp();

					if(curg->queue && *curg->queue) {
						exec_queue_add(curg->queue, argv, envp);
					} else {
						forkexec(argv, envp);

						exec_queue_argv_free(argv);
						exec_queue_envp_free(envp);
					}
				}
				if(event_script_check(curg->notifyscript)) {
					char **argv;
					char **envp;

					argv = exec_queue_argv("%s %s %s %s %s %s %d %d %d %d %d %d %d %d %s %s %d",
							       curg->notifyscript,
							       get_status_str(curg->status),
							       curg->name,
							       "",
							       curg->device ? curg->device : "",
							       curg->warn_email ? curg->warn_email : "",
							       0,
							       0,
							       0,
							       0,
							       0,
							       0,
							       0,
							       0,
							       "",
							       get_status_str(prevstatus),
							       current_time.tv_sec);
					envp = exec_queue_envp();

					forkexec(argv, envp);

					exec_queue_argv_free(argv);
					exec_queue_envp_free(envp);
				}
			}
		}
		curg = curg->next;
	}
}

static int wait_for_replies(CONFIG **ctable) {
	struct ip *ip;
	int hlen = 0;
	struct icmp *icp;
	struct icmp6_hdr *icp6;
	PING_DATA *pdp;
	int this_count;
	struct timeval sent_time = {0, 0};
	struct timeval current_time = {0, 0};
	long time_diff;
	char buf[BUFSIZ];
	union {
		struct sockaddr_in6 saddr6;
		struct sockaddr_in saddr;
		struct sockaddr_ll FROM;
	} from_addr;
	unsigned int slen;
	int result;
	TARGET *t;
	int seq;
	CONFIG *arp;

	slen = sizeof(from_addr);
	result = ping_rcv(ctable[0], buf, BUFSIZ, (struct sockaddr_in6 *)&from_addr, &slen, DEFAULT_SELECT_WAIT, &arp);

	if(result <= 0) {
		return(0);
	}

	gettimeofday(&current_time, NULL);

	if(arp) {
		struct sockaddr_ll *FROM = &from_addr.FROM;
		TARGET *t = arp->data;
		struct arphdr *ah = (struct arphdr*)buf;
		unsigned char *p = (unsigned char *)(ah+1);
		struct in_addr src_ip, dst_ip;
		int ind;

		/* Filter out wild packets */
		if(FROM->sll_pkttype != PACKET_HOST &&
		   FROM->sll_pkttype != PACKET_BROADCAST &&
		   FROM->sll_pkttype != PACKET_MULTICAST)
			return(1);

		/* Only these types are recognised */
#if 0
		if(ah->ar_op != htons(ARPOP_REQUEST) &&
		   ah->ar_op != htons(ARPOP_REPLY))
			return(1);
#else
		if(ah->ar_op != htons(ARPOP_REPLY))
			return(1);
#endif

		/* ARPHRD check and this darned FDDI hack here :-( */
		if(ah->ar_hrd != htons(FROM->sll_hatype) &&
		   (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER)))
			return(1);

		/* Protocol must be IP. */
		if(ah->ar_pro != htons(ETH_P_IP))
			return(1);
		if(ah->ar_pln != 4)
			return(1);
		if(ah->ar_hln != t->me.sll_halen)
			return(1);

#if defined(DEBUG)
		for(ind = 0; ind < result; ind ++) {
			fprintf(stderr, "%02x", (unsigned char)buf[ind]);
			if(!((ind + 1) % 2)) fprintf(stderr, " ");
			if(!((ind + 1) % 32)) fprintf(stderr, "\n");
		}
		fprintf(stderr, "\n");
		for(ind = 0; ind < result; ind ++) {
			fprintf(stderr, "%3u", (unsigned char)buf[ind]);
			if(!((ind + 1) % 32)) fprintf(stderr, "\n");
			else fprintf(stderr, ",");
		}
		fprintf(stderr, "\n");
#endif

		if(result < sizeof(*ah) + 2*(4 + ah->ar_hln))
			return(1);

		memcpy(&src_ip, p+ah->ar_hln, 4);
		memcpy(&dst_ip, p+ah->ar_hln+4+ah->ar_hln, 4);

		if(src_ip.s_addr != t->dst.s_addr)
			return(1);
		if(t->src.s_addr != dst_ip.s_addr)
			return(1);
		if(memcmp(p+ah->ar_hln+4, &t->me.sll_addr, ah->ar_hln))
			return(1);

		/* update packet log here */
		/* there are no sequence numbers in arp replies so just mark seq - 1 replied */
		ind = ((t->seq - 1) >= 0 ? (t->seq - 1) : (FOLLOWED_PKTS + (t->seq - 1))) % FOLLOWED_PKTS;
		t->sentpkts[ind].flags.replied = 1;
		t->sentpkts[ind].flags.waiting = 0;
		t->sentpkts[ind].replied_time = current_time;
		t->sentpkts[ind].rtt = timeval_diff(&current_time, &t->sentpkts[ind].sent_time);

		return(1);
	}

#if defined(DEBUG)
	if(cfg.debug >= 9) {
		char sbuf[INET6_ADDRSTRLEN];

		syslog(LOG_INFO, "not arp: family = %d, AF_INET = %d, AF_INET6 = %d, inet_ntop addr = %s, inet_ntoa addr = %s", from_addr.saddr6.sin6_family, AF_INET, AF_INET6, inet_ntop(from_addr.saddr6.sin6_family, &from_addr.saddr6.sin6_addr, sbuf, INET6_ADDRSTRLEN), inet_ntoa(from_addr.saddr.sin_addr));
	}
#endif

	switch(from_addr.saddr6.sin6_family) {
	case AF_INET:
#if defined(DEBUG)
		syslog(LOG_INFO, "%s: %s: AF_INET reply", __FILE__, __FUNCTION__);
#endif
		ip = (struct ip *)buf;
		hlen = ip->ip_hl << 2;

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

		if(icp->icmp_type == ICMP_ECHO) {
			return(1);
		}

		if(icp->icmp_type == ICMP_ECHOREPLY) {
			if(icp->icmp_id != get_ident()) {
				/* fprintf(stderr, "icmp_id = %d funny, got reply from %s to something else ...\n", icp->icmp_id, inet_ntoa(saddr.sin_addr)); */
				return(1);
			}

			if(result < sizeof(struct icmp) + sizeof(PING_DATA)) {
				/* fprintf(stderr, "too short ping reply\n"); */
				return(1);
			}

			pdp = (PING_DATA *)(buf + hlen + sizeof(struct icmp));

			this_count = pdp->ping_count;
			sent_time = pdp->ping_ts;
			time_diff = timeval_diff(&current_time, &sent_time);

			if(pdp->id >= num_hosts) {
#if defined(DEBUG)
				syslog(LOG_INFO, "out of range: pdp->id = %d >= num_hosts = %d from %s", pdp->id, num_hosts, inet_ntoa(from_addr.saddr.sin_addr));
				dump_pkt(buf, sizeof(struct ip) + sizeof(struct icmp) + sizeof(PING_DATA));
				set_dump(1);
#endif
				return(1);
			}

			t = ctable[pdp->id]->data;

			if(memcmp(&ip->ip_src, &t->dst, sizeof(struct in_addr) != 0)) {
				return(1);
			}

			seq = icp->icmp_seq % FOLLOWED_PKTS;
			if(t->sentpkts[seq].seq == icp->icmp_seq) {
				t->sentpkts[seq].flags.replied = 1;
				t->sentpkts[seq].flags.waiting = 0;
				t->sentpkts[seq].replied_time = current_time;
				t->sentpkts[seq].rtt = timeval_diff(&current_time, &t->sentpkts[seq].sent_time);
			}
			else
				if(cfg.debug >= 9) syslog(LOG_INFO, "sentpkts seq != icmp_seq");

			if(cfg.debug >= 9) syslog(LOG_INFO, "received seq = %d from %s, id = %d, num_sent = %d, target id = %u, time_diff = %ld", icp->icmp_seq, inet_ntoa(from_addr.saddr.sin_addr), icp->icmp_id, this_count, pdp->id, time_diff);

			return(1);

		} else {
			struct icmpmsg *msg;

			msg = stricmp(icp->icmp_type, icp->icmp_code);

			if(cfg.debug >= 9) syslog(LOG_INFO, "got odd reply from %s, icmp_type = %d %s, icmp_code = %d %s", inet_ntoa(from_addr.saddr.sin_addr), icp->icmp_type, msg->type_msg, icp->icmp_code, msg->code_msg);

			return(1);
		}
		break;
	case AF_INET6:
#if defined(DEBUG)
		syslog(LOG_INFO, "%s: %s: AF_INET6 reply", __FILE__, __FUNCTION__);
#endif
		icp6 = (struct icmp6_hdr *)buf;

		if(icp6->icmp6_type == ICMP6_ECHO_REQUEST) {
			return(1);
		}

		if (icp6->icmp6_type == ICMP6_ECHO_REPLY) { /* v6 reply */
			char sbuf[INET6_ADDRSTRLEN];
#if defined(DEBUG)
			dump_pkt(buf, sizeof(struct icmp6_hdr) + sizeof(PING_DATA));
#endif
			/* syslog(LOG_INFO, "sizeof struct icmp6_hdr = %ld\n", sizeof(struct icmp6_hdr)); */

			if(icp6->icmp6_id != get_ident()) {
				return(1);
			}

			if(result < sizeof(struct icmp6_hdr) + sizeof(PING_DATA)) {
				return(1);
			}

			/* pdp = (PING_DATA *)(buf + hlen + sizeof(struct icmp6_hdr)); */
			/* pdp = (PING_DATA *)(buf + sizeof(struct icmp6_hdr)); */
			pdp = (PING_DATA *)(buf + sizeof(struct icmp6_hdr));

			this_count = pdp->ping_count;
			sent_time = pdp->ping_ts;
			time_diff = timeval_diff(&current_time, &sent_time);

#if defined(DEBUG)
			syslog(LOG_INFO, "%s: %s: this_count = %d, sent_time = %ld,%ld, pdp->id = %d", __FILE__, __FUNCTION__, this_count, sent_time.tv_sec, sent_time.tv_usec, pdp->id);
#endif

			if(pdp->id >= num_hosts) {
#if defined(DEBUG)
				syslog(LOG_INFO, "out of range: pdp->id = %d >= num_hosts = %d from %s", pdp->id, num_hosts, inet_ntop(AF_INET6, &from_addr.saddr6.sin6_addr, sbuf, INET6_ADDRSTRLEN));
				dump_pkt(buf, sizeof(struct icmp6_hdr) + sizeof(PING_DATA));
				set_dump(1);
#endif
				return(1);
			}

			t = ctable[pdp->id]->data;

			if(memcmp(&from_addr.saddr6.sin6_addr, &t->dst6, sizeof(struct in6_addr)) != 0) {
				return(1);
			}

			seq = ntohs(icp6->icmp6_seq) % FOLLOWED_PKTS;
			if(t->sentpkts[seq].seq == ntohs(icp6->icmp6_seq)) {
				t->sentpkts[seq].flags.replied = 1;
				t->sentpkts[seq].flags.waiting = 0;
				t->sentpkts[seq].replied_time = current_time;
				t->sentpkts[seq].rtt = timeval_diff(&current_time, &t->sentpkts[seq].sent_time);
			}
			else
				if (cfg.debug >= 9) syslog(LOG_INFO, "sentpkts seq != icmp_seq");

			if(cfg.debug >= 9) syslog(LOG_INFO, "received seq = %d from %s, id = %d, num_sent = %d, target id = %u, time_diff = %ld", ntohs(icp6->icmp6_seq), inet_ntop(AF_INET6, &from_addr.saddr6.sin6_addr, sbuf, INET6_ADDRSTRLEN), icp6->icmp6_id, this_count, pdp->id, time_diff);

			return(1);
		} else {
			char sbuf[INET6_ADDRSTRLEN];
			struct icmp6msg *msg;

			msg = stricmp6(icp6->icmp6_type, icp6->icmp6_code);

			if(cfg.debug >= 9) syslog(LOG_INFO, "got odd reply from %s, icmp_type = %d %s, icmp_code = %d %s", inet_ntop(from_addr.saddr6.sin6_family, &from_addr.saddr6.sin6_addr, sbuf, INET6_ADDRSTRLEN), icp6->icmp6_type, msg->type_msg, icp6->icmp6_code, msg->code_msg);

			return(1);
		}
		break;
	default:
		syslog(LOG_INFO, "%s: %s: unknown family reply", __FILE__, __FUNCTION__);
		break;
	}

	return(1);
}

static int ping_rcv(CONFIG *first, char *buf, int len, struct sockaddr_in6 *saddr, unsigned int *slen, long usec, CONFIG **arp) {
	int nfound, n;
	fd_set readset;
	struct timeval to = {0, 0};
	int max;
	CONFIG *cur;
	TARGET *t;
	int cnt_targets = 0;

	FD_ZERO(&readset);
	max = 0;

	for(cur = first; cur; cur = cur->next) {
		t = cur->data;
		if(t->sock == -1) continue;
		if(t->sock > max) max = t->sock;
		FD_SET(t->sock, &readset);
		cnt_targets++;
	}

	/* no point in calling select if we didn't find any open sockets. so sleep and return ... */
	if(cnt_targets == 0) {
		sleep(1);
		return(0);
	}

	to.tv_sec = usec / 1000000;
	to.tv_usec = (usec - (to.tv_sec * 1000000));

#if defined(DEBUG)
	printf("to.tv_sec = %ld, to.tv_usec = %ld\n", to.tv_sec, to.tv_usec);
#endif

	nfound = select(max + 1, &readset, NULL, NULL, &to);

	if(nfound < 0) {
		if(errno != EINTR) syslog(LOG_INFO, "select failed \"%s\"", strerror(errno));
		return(0);
	}

	if(nfound == 0) return(-1);

	for(cur = first; cur; cur = cur->next) {
		t = cur->data;
		if(t->sock == -1) continue;
		if(FD_ISSET(t->sock, &readset)) {
			if(!cur->check_arp) {
				*arp = (CONFIG *)NULL;
			} else {
				*arp = cur;
			}

			n = recvfrom(t->sock, buf, len, 0, (struct sockaddr *)saddr, slen);

			if(n < 0) {
				if(cfg.debug >= 9) syslog(LOG_INFO, "recvfrom failed with connection %s \"%s\", n = %d, errno = %d", cur->name, strerror(errno), n, errno);
				close(t->sock);
				t->sock = -1;
				return(0);
			}

			return(n);
		}
	}
	return(0);
}

static int ping_send(CONFIG *cur) {
	char buf[BUFSIZ];
	struct icmp *icp;
	PING_DATA *pdp;
	TARGET *t;
	int n;
	int ping_pkt_size;

	t = cur->data;

	gettimeofday(&t->last_send_time, NULL);

	if(cur->check_arp) {
		int err;
		unsigned char buf[256];
		struct arphdr *ah = (struct arphdr*)buf;
		unsigned char *p = (unsigned char *)(ah+1);

		if(cur->dstinfo->ai_family == AF_INET6) {
			syslog(LOG_ERR, "%s: %s: ipv6 arping not supported", __FILE__, __FUNCTION__);
			return(-1);
		}

		ah->ar_hrd = htons(t->me.sll_hatype);
		if(ah->ar_hrd == htons(ARPHRD_FDDI))
			ah->ar_hrd = htons(ARPHRD_ETHER);
		ah->ar_pro = htons(ETH_P_IP);
		ah->ar_hln = t->me.sll_halen;
		ah->ar_pln = 4;
		ah->ar_op = htons(ARPOP_REQUEST);

		memcpy(p, &t->me.sll_addr, ah->ar_hln);
		p += t->me.sll_halen;

		memcpy(p, &t->src, 4);
		p += 4;

		memcpy(p, &t->he.sll_addr, ah->ar_hln);
		p += ah->ar_hln;

		memcpy(p, &t->dst, 4);
		p += 4;

#if defined(DEBUG)
		{
			int ind;
			for(ind = 0; ind < p - buf; ind ++) {
				fprintf(stderr, "%02x", (unsigned char)buf[ind]);
				if(!((ind + 1) % 2)) fprintf(stderr, " ");
				if(!((ind + 1) % 32)) fprintf(stderr, "\n");
			}
			fprintf(stderr, "\n");
			for(ind = 0; ind < p - buf; ind ++) {
				fprintf(stderr, "%3u", (unsigned char)buf[ind]);
				if(!((ind + 1) % 32)) fprintf(stderr, "\n");
				else fprintf(stderr, ",");
			}
			fprintf(stderr, "\n");
		}
#endif

		if(t->sock != -1) {
			err = sendto(t->sock, buf, p - buf, 0, (struct sockaddr*)&t->he, sizeof(t->he));
			if(err < 0) {
				if(cfg.debug >= 9) syslog(LOG_ERR, "arping sendto failed to %s on %s reason \"%s\"", cur->name, cur->device, strerror(errno));
				close(t->sock);
				t->sock = -1;
			}
		} else {
			if(cfg.debug >= 9) syslog(LOG_INFO, "arping sendto socket not open for %s", cur->name);
			err = -1;
		}

		{ /* we don't care what the error was just advance with seq */
			int seq;

			seq = t->seq % FOLLOWED_PKTS;
			t->sentpkts[seq].seq = t->seq;
			t->sentpkts[seq].sent_time = t->last_send_time;
			t->sentpkts[seq].flags.replied = 0;
			t->sentpkts[seq].flags.timeout = 0;
			t->sentpkts[seq].flags.waiting = 1;
			if(t->sentpkts[seq].flags.used == 0) t->used++;
			t->sentpkts[seq].flags.used = 1;
			t->sentpkts[seq].flags.error = (err == -1) ? 1 : 0;

			t->seq = (t->seq + 1) % SEQ_LIMITER; /* limit seq so that consecutive missing and received pkt counting doesn't get confused when seq "overflows" */
			t->num_sent++;

		}
		if(err == (p - buf)) {
			return(0);
		}
		return(err);
	}

	if(cur->dstinfo->ai_family == AF_INET6) {
		struct icmp6_hdr *icp6;

		ping_pkt_size = sizeof(struct icmp6_hdr) + sizeof(PING_DATA);

		memset(buf, 0, ping_pkt_size);

		icp6 = (struct icmp6_hdr *)buf;

		icp6->icmp6_type = ICMP6_ECHO_REQUEST;
		icp6->icmp6_code = 0;
		icp6->icmp6_seq = htons(t->seq); /* I saw a tcpdump suggesting that there is something wrong with seq thus htons() */
		icp6->icmp6_id = get_ident();

		pdp = (PING_DATA *)(buf + sizeof(struct icmp6_hdr));
		pdp->ping_count = t->num_sent;
		pdp->ping_ts = t->last_send_time;
		pdp->id = t->id;

		icp6->icmp6_cksum = 0; /* the ipv6 stack calculates the checksum for us */

		if(t->sock != -1) {
			if(cfg.debug >= 9) syslog(LOG_INFO, "cmsglen = %d", t->cmsglen);

			if(t->cmsglen == 0) {
				n = sendto(t->sock, buf, ping_pkt_size, 0, (struct sockaddr *)&t->dst_addr6, sizeof(t->dst_addr6));
				if(n < 0) {
					if(errno == ENODEV) {
						if(cfg.debug >= 9) syslog(LOG_ERR, "connection %s no such device %s \"%s\"", cur->name, cur->device, strerror(errno));
					} else
						if (cfg.debug >= 9) syslog(LOG_ERR, "ping6 sendto failed to %s on %s reason \"%s\"", cur->name, cur->device, strerror(errno));

					if(t->sock != -1) {
						close(t->sock);
						t->sock = -1;
					}
				}
			} else {
				struct msghdr mhdr;
				struct iovec iov;
				int confirm = 0;

				iov.iov_len = ping_pkt_size;
				iov.iov_base = buf;

				mhdr.msg_name = &t->dst_addr6;
				mhdr.msg_namelen = sizeof(struct sockaddr_in6);
				mhdr.msg_iov = &iov;
				mhdr.msg_iovlen = 1;
				mhdr.msg_control = t->cmsgbuf;
				mhdr.msg_controllen = t->cmsglen;

				n = sendmsg(t->sock, &mhdr, confirm);
				if(cfg.debug >= 9 && n < 0) syslog(LOG_INFO, "sendmsg failed for %s %s", cur->name, strerror(errno));
				if(n < 0) {
					close(t->sock);
					t->sock = -1;
				}
			}
		} else {
			if(cfg.debug >= 9) syslog(LOG_INFO, "ping sendto socket not open for %s", cur->name);
			n = -1;
		}
		{
			/* we don't care what the error was just advance with seq */
			int seq;

			seq = t->seq % FOLLOWED_PKTS;
#if defined(DEBUG)
			fprintf(stderr, "ping_send seq = %d to %s, num_sent = %ld, %ld, pkt_size = %d\n", t->seq, inet_ntoa(t->dst_addr.sin_addr), t->num_sent, pdp->ping_count, ping_pkt_size);
#endif
			t->sentpkts[seq].seq = t->seq;
			t->sentpkts[seq].sent_time = t->last_send_time;
			t->sentpkts[seq].flags.replied = 0;
			t->sentpkts[seq].flags.timeout = 0;
			t->sentpkts[seq].flags.waiting = 1;
			if(t->sentpkts[seq].flags.used == 0) t->used++;
			t->sentpkts[seq].flags.used = 1;
			t->sentpkts[seq].flags.error = (n < 1) ? 1 : 0;

			t->seq = (t->seq + 1) % SEQ_LIMITER;
			/* limit seq so that consecutive missing and received pkt counting doesn't get confused when seq "overflows" */
			t->num_sent++;
		}
		if(n == ping_pkt_size) {
			return(0);
		}

		return(n);
	}

	/* send a ping packet */
	ping_pkt_size = sizeof(struct icmp) + sizeof(PING_DATA);

	memset(buf, 0, ping_pkt_size);

	icp = (struct icmp *)buf;

	icp->icmp_type = ICMP_ECHO;
	icp->icmp_code = 0;
	icp->icmp_cksum = 0;
	icp->icmp_seq = t->seq;
	icp->icmp_id = get_ident();

	pdp = (PING_DATA *)(buf + sizeof(struct icmp));
	pdp->ping_count = t->num_sent;
	pdp->ping_ts = t->last_send_time;
	pdp->id = t->id;

	icp->icmp_cksum = in_cksum((u_short *)icp, ping_pkt_size);

	if(t->sock != -1) {
		n = sendto(t->sock, buf, ping_pkt_size, 0, (struct sockaddr *)&t->dst_addr, sizeof(struct sockaddr));

		if(n < 0) {
			if(errno == ENODEV) {
				if(cfg.debug >= 9) syslog(LOG_ERR, "connection %s no such device %s \"%s\"", cur->name, cur->device, strerror(errno));
				/* exit(2); */ /* commented out. handle this situation like the packet had been sent. see below.  */
			}
			else
				if(cfg.debug >= 9) syslog(LOG_ERR, "ping sendto failed to %s on %s reason \"%s\"", cur->name, cur->device, strerror(errno));

			if(t->sock != -1) {
				close(t->sock);
				t->sock = -1;
			}
		}
	} else {
		if(cfg.debug >= 9) syslog(LOG_INFO, "ping sendto socket not open for %s", cur->name);
		n = -1;
	}

	{ /* we don't care what the error was just advance with seq */
		int seq;

		seq = t->seq % FOLLOWED_PKTS;
#if defined(DEBUG)
		fprintf(stderr, "ping_send seq = %d to %s, num_sent = %ld, %ld, pkt_size = %d\n", t->seq, inet_ntoa(t->dst_addr.sin_addr), t->num_sent, pdp->ping_count, ping_pkt_size);
#endif
		t->sentpkts[seq].seq = t->seq;
		t->sentpkts[seq].sent_time = t->last_send_time;
		t->sentpkts[seq].flags.replied = 0;
		t->sentpkts[seq].flags.timeout = 0;
		t->sentpkts[seq].flags.waiting = 1;
		if(t->sentpkts[seq].flags.used == 0) t->used++;
		t->sentpkts[seq].flags.used = 1;
		t->sentpkts[seq].flags.error = (n < 1) ? 1 : 0;

		t->seq = (t->seq + 1) % SEQ_LIMITER; /* limit seq so that consecutive missing and received pkt counting doesn't get confused when seq "overflows" */
		t->num_sent++;
	}

	if(n == ping_pkt_size) {
		return(0);
	}
	return(n);
}

static int event_script_check(const char *path)
{
	struct stat statbuf;

	if(!path) {
		if(cfg.debug >= 9) syslog(LOG_ERR, "NULL pointer event script");
		return(0);
	}

	if(!*path) {
		if(cfg.debug >= 9) syslog(LOG_ERR, "null string event script");
		return(0);
	}

	/* check that the script is owner executable */
	if(stat(path, &statbuf) == -1) {
		syslog(LOG_ERR, "failed to stat event script \"%s\" reason \"%s\"", path, strerror(errno));
		return(0);
	}

	if((statbuf.st_mode & S_IXUSR) == 0) {
		syslog(LOG_ERR, "event script \"%s\" is not executable by owner, please check permissions", path);
		return(0);
	}

	return(1);
}

static void init_config_data(CONFIG *first, CONFIG *last, CONFIG ***ctable)
{
	int i;
	CONFIG *cur;
	TARGET *t = NULL;

	/* initialize config->data */
	for(cur = first, num_hosts = 0; cur; cur = cur->next, num_hosts++) {
		u_int ipaddress;

		if((t = malloc(sizeof(TARGET))) == NULL) {
			syslog(LOG_ERR, "main: initializing targets failed to malloc");
			exit(1);
		}
		memset(t, 0, sizeof(TARGET));

		cur->data = t;

		/* protocol family independent init */
		t->seq = 0;
		t->downseq = 0;
		t->downseqreported = 0;
		t->last_send_time.tv_sec = 0;
		t->last_send_time.tv_usec = 0;
		t->num_sent = 0;
		t->timeout_max = 0;
		t->consecutive_missing_max = 0;
		t->used = 0;

		memset(t->cmsgbuf, 0, sizeof(t->cmsgbuf));
		t->cmsglen = 0;

		t->id = num_hosts;

		/* get initial connection state assumption from config */
		t->status = cur->status;

		t->sock = -1;

		if(cur->dstinfo->ai_family == AF_INET6) {
			/* ipv6 init */
			if(cur->srcinfo) {
				if(inet_pton(AF_INET6, cur->sourceip, &t->src6) != 1) {
					syslog(LOG_ERR, "%s: %s: src6 inet_pton failed for %s", __FILE__, __FUNCTION__, cur->name);
				}

				t->src_addr6.sin6_family = cur->srcinfo->ai_family;
				if(inet_pton(AF_INET6, cur->sourceip, &t->src_addr6.sin6_addr) != 1) {
					syslog(LOG_ERR, "%s: %s: src6 inet_pton failed for %s", __FILE__, __FUNCTION__, cur->name);
				}
			}

			if(inet_pton(AF_INET6, cur->checkip, &t->dst6) != 1) {
				syslog(LOG_ERR, "%s: %s: dst6 inet_pton failed for %s", __FILE__, __FUNCTION__, cur->name);
			}

			t->dst_addr6.sin6_family = cur->dstinfo->ai_family;
			if(inet_pton(AF_INET6, cur->checkip, &t->dst_addr6.sin6_addr) != 1) {
				syslog(LOG_ERR, "%s: %s: dst6 inet_pton failed for %s", __FILE__, __FUNCTION__, cur->name);
			}
		} else {
			/* ipv4 init */
			ipaddress = inet_addr(cur->checkip);
			t->dst_addr.sin_family = AF_INET;
			t->dst_addr.sin_addr = *((struct in_addr *)&ipaddress);
			t->dst = *((struct in_addr *)&ipaddress);
			if(cur->srcinfo) {
				if(inet_pton(AF_INET, cur->sourceip, &t->src) != 1) {
					syslog(LOG_ERR, "%s: %s: src inet_pton failed for %s", __FILE__, __FUNCTION__, cur->name);
				}
			}
		}
	}

	if(((*ctable) = (CONFIG **)malloc(sizeof(CONFIG *) * num_hosts)) == NULL) {
		syslog(LOG_ERR, "main: can't malloc for ctable");
		exit(1);
	}

	/* create pointer table */
	for(cur = first, i = 0; cur; cur = cur->next, i++) {
		(*ctable)[i] = cur;
	}

}

static int open_arp_sock(CONFIG *cur)
{
	int ifindex = 0;
	TARGET *t = (TARGET *)cur->data;

	if(t->sock != -1) return(0);

	if(cur->dstinfo->ai_family == AF_INET6) {
		syslog(LOG_ERR, "%s: %s: protocol family is ipv6?", __FILE__, __FUNCTION__);
		return(1);
	}

	t->sock = socket(PF_PACKET, SOCK_DGRAM, 0);
	if(t->sock < 0) {
		syslog(LOG_ERR, "could not open socket for %s arp ping \"%s\"", cur->name, strerror(errno));
		t->sock = -1;
		return(1);
	}
	if(fcntl(t->sock, F_SETFD, FD_CLOEXEC) == -1) {
		syslog(LOG_ERR, "failed to set close on exec on socket %s reason \"%s\"", cur->name, strerror(errno));
	}

	if(cur->device && *cur->device) {
		struct ifreq ifr;

		memset(&ifr, 0, sizeof(ifr));
		strncpy(ifr.ifr_name, cur->device, IFNAMSIZ-1);
		if(ioctl(t->sock, SIOCGIFINDEX, &ifr) < 0) {
			syslog(LOG_ERR, "unknown iface \"%s\"", cur->device);
			close(t->sock);
			t->sock = -1;
			return(2);
		}
		ifindex = ifr.ifr_ifindex;

		if(ioctl(t->sock, SIOCGIFFLAGS, (char*)&ifr)) {
			syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS) \"%s\"", strerror(errno));
			close(t->sock);
			t->sock = -1;
			return(2);
		}
		if(!(ifr.ifr_flags&IFF_UP)) {
			syslog(LOG_ERR, "Interface \"%s\" is down", cur->device);
			close(t->sock);
			t->sock = -1;
			return(2);
		}
		if(ifr.ifr_flags&(IFF_NOARP|IFF_LOOPBACK)) {
			syslog(LOG_ERR, "Interface \"%s\" is not ARPable", cur->device);
			close(t->sock);
			t->sock = -1;
			return(2);
		}
	}

	if(inet_aton(cur->checkip, &t->dst) != 1) {
		struct hostent *hp;
		hp = gethostbyname2(cur->checkip, AF_INET);
		if(!hp) {
			syslog(LOG_ERR, "unknown host %s\n", cur->checkip);
			close(t->sock);
			t->sock = -1;
			return(2);
		}
		memcpy(&t->dst, hp->h_addr, 4);
	}

	if(cur->sourceip && *cur->sourceip)
		if(inet_aton(cur->sourceip, &t->src) != 1) {
			syslog(LOG_ERR, "invalid source %s\n", cur->sourceip);
			close(t->sock);
			t->sock = -1;
			return(2);
		}

	if(probe_src_ip_addr(cur) != 0) {
		close(t->sock);
		t->sock = -1;
		return(2);
	}

	t->me.sll_family = AF_PACKET;
	t->me.sll_ifindex = ifindex;
	t->me.sll_protocol = htons(ETH_P_ARP);
	if(bind(t->sock, (struct sockaddr*)&t->me, sizeof(t->me)) == -1) {
		syslog(LOG_ERR, "bind \"%s\"", strerror(errno));
		close(t->sock);
		t->sock = -1;
		return(2);
	}

	{
		int alen = sizeof(t->me);
		if(getsockname(t->sock, (struct sockaddr*)&t->me, (socklen_t*)&alen) == -1) {
			syslog(LOG_ERR, "getsockname \"%s\"", strerror(errno));
			close(t->sock);
			t->sock = -1;
			return(2);
		}
	}
	if(t->me.sll_halen == 0) {
		syslog(LOG_ERR, "Interface \"%s\" is not ARPable (no ll address)", cur->device);
		close(t->sock);
		t->sock = -1;
		return(2);
	}

	t->he = t->me;
	memset(t->he.sll_addr, -1, min(t->he.sll_halen, sizeof t->he.sll_addr));

#if 0
	printf("ARPING %s ", inet_ntoa(t->dst));
	printf("from %s %s\n",  inet_ntoa(t->src), cur->device ? : "");
#endif

	if(!t->src.s_addr) {
		syslog(LOG_ERR, "no source address for %s", cur->name);
		close(t->sock);
		t->sock = -1;
		return(2);
	}
	if(cur->ttl) {
		int ittl = cur->ttl;
		if(setsockopt(t->sock, IPPROTO_IP, IP_MULTICAST_TTL,
			      &cur->ttl, 1) == -1) {
			syslog(LOG_ERR, "can't set multicast time-to-live \"%s\"", strerror(errno));
			close(t->sock);
			t->sock = -1;
			return(2);
		}
		if(setsockopt(t->sock, IPPROTO_IP, IP_TTL,
			      &ittl, sizeof(ittl)) == -1) {
			syslog(LOG_ERR, "can't set unicast time-to-live \"%s\"", strerror(errno));
			close(t->sock);
			t->sock = -1;
			return(2);
		}
	}

	return(0);
}

static int open_icmp_sock(CONFIG *cur)
{
	TARGET *t = (TARGET *)cur->data;
	struct protoent *proto;
	int pf = cur->dstinfo->ai_family;

	if(t->sock != -1) return(0);

	if(pf == AF_INET6) {
		if((proto = getprotobyname("ipv6-icmp")) == NULL) {
			syslog(LOG_ERR, "no ipv6-icmp proto found");
			return(1);
		}
	} else {
		if((proto = getprotobyname("icmp")) == NULL) {
			syslog(LOG_ERR, "no icmp proto found");
			return(1);
		}
	}

	t->sock = socket(pf, SOCK_RAW, proto->p_proto);

	if(t->sock < 0) {
		syslog(LOG_ERR, "could not open socket for ping target \"%s\" reason \"%s\"\n", cur->name, strerror(errno));
		t->sock = -1;
		return(1);
	}
	if(fcntl(t->sock, F_SETFD, FD_CLOEXEC) == -1) {
		syslog(LOG_ERR, "failed to set close on exec on socket %s reason \"%s\"", cur->name, strerror(errno));
	}

	if(pf == AF_INET6) {
		int opton = 1;

#ifdef IPV6_RECVHOPOPTS
		if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_RECVHOPOPTS, &opton,
			      sizeof(opton))) {
			syslog(LOG_ERR, "setsockopt(IPV6_RECVHOPOPTS)");
			close(t->sock);
			t->sock = -1;
			return(2);
		}
#else  /* old adv. API */
		if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_HOPOPTS, &opton,
			      sizeof(opton))) {
			syslog(LOG_ERR, "setsockopt(IPV6_HOPOPTS)");
			close(t->sock);
			t->sock = -1;
			return(s);
		}
#endif
#ifdef IPV6_RECVDSTOPTS
		if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_RECVDSTOPTS, &opton,
			      sizeof(opton))) {
			syslog(LOG_ERR, "setsockopt(IPV6_RECVDSTOPTS)");
			close(t->sock);
			t->sock = -1;
			return(2);
		}
#else  /* old adv. API */
		if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_DSTOPTS, &opton,
			      sizeof(opton))) {
			syslog(LOG_ERR, "setsockopt(IPV6_DSTOPTS)");
			close(t->sock);
			t->sock = -1;
			return(2);
		}
#endif
#ifdef IPV6_RECVRTHDRDSTOPTS
		if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_RECVRTHDRDSTOPTS, &opton,
			      sizeof(opton))) {
			syslog(LOG_ERR, "setsockopt(IPV6_RECVRTHDRDSTOPTS)");
			close(t->sock);
			t->sock = -1;
			return(2);
		}
#endif
#ifdef IPV6_RECVRTHDR
		if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_RECVRTHDR, &opton,
			      sizeof(opton))) {
			syslog(LOG_ERR, "setsockopt(IPV6_RECVRTHDR)");
			close(t->sock);
			t->sock = -1;
			return(2);
		}
#else  /* old adv. API */
		if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_RTHDR, &opton,
			      sizeof(opton))) {
			syslog(LOG_ERR, "setsockopt(IPV6_RTHDR)");
			close(t->sock);
			t->sock = -1;
			return(2);
		}
#endif
#ifndef USE_SIN6_SCOPE_ID
#ifdef IPV6_RECVPKTINFO
		if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &opton,
			      sizeof(opton))) {
			syslog(LOG_ERR, "setsockopt(IPV6_RECVPKTINFO)");
			close(t->sock);
			t->sock = -1;
			return(2);
		}
#else  /* old adv. API */
		if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_PKTINFO, &opton,
			      sizeof(opton))) {
			syslog(LOG_ERR, "setsockopt(IPV6_PKTINFO)");
			close(t->sock);
			t->sock = -1;
			return(2);
		}
#endif
#endif /* USE_SIN6_SCOPE_ID */
#ifdef IPV6_RECVHOPLIMIT
		if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &opton,
			      sizeof(opton))) {
			syslog(LOG_ERR, "setsockopt(IPV6_RECVHOPLIMIT)");
			close(t->sock);
			t->sock = -1;
			return(2);
		}
#else  /* old adv. API */
		if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &opton,
			      sizeof(opton))) {
			syslog(LOG_ERR, "setsockopt(IPV6_HOPLIMIT)");
			close(t->sock);
			t->sock = -1;
			return(2);
		}
#endif
#ifdef IPV6_CHECKSUM
#ifndef SOL_RAW
#define SOL_RAW IPPROTO_IPV6
#endif
		opton = 2;

		if(setsockopt(t->sock, SOL_RAW, IPV6_CHECKSUM, &opton,
			      sizeof(opton))) {
			syslog(LOG_ERR, "setsockopt(SOL_RAW,IPV6_CHECKSUM)");
			close(t->sock);
			t->sock = -1;
			return(2);
		}
#endif
	}

	if(pf == AF_INET6) {
		int hold = 1;

		ICMP6_FILTER_SETBLOCKALL(&t->filter);

		if (setsockopt(t->sock, SOL_IPV6, IPV6_RECVERR, (char *)&hold, sizeof(hold))) {
			syslog(LOG_INFO, "WARNING: your kernel is veeery old. No problems.");

			ICMP6_FILTER_SETPASS(ICMP6_DST_UNREACH, &t->filter);
			ICMP6_FILTER_SETPASS(ICMP6_PACKET_TOO_BIG, &t->filter);
			ICMP6_FILTER_SETPASS(ICMP6_TIME_EXCEEDED, &t->filter);
			ICMP6_FILTER_SETPASS(ICMP6_PARAM_PROB, &t->filter);
		}

		ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &t->filter);

		if(setsockopt(t->sock, IPPROTO_ICMPV6, ICMP6_FILTER, &t->filter, sizeof(struct icmp6_filter)) < 0) {
			syslog(LOG_ERR, "setsockopt(ICMP6_FILTER)");
			return(2);
		}
	}

	if(cur->ttl) {
		if(pf == AF_INET6) {
			if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
				       &cur->ttl, sizeof(cur->ttl)) == -1) {
				syslog(LOG_ERR, "can't set multicast hop limit \"%s\"", strerror(errno));
				close(t->sock);
				t->sock = -1;
				return(2);
			}
			if(setsockopt(t->sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
				       &cur->ttl, sizeof(cur->ttl)) == -1) {
				syslog(LOG_ERR, "can't set unicast hop limit \"%s\"", strerror(errno));
				close(t->sock);
				t->sock = -1;
				return(2);
			}
		} else if(pf == AF_INET) { /* AF_INET */
			int ittl = cur->ttl;
			if(setsockopt(t->sock, IPPROTO_IP, IP_MULTICAST_TTL,
			      &cur->ttl, 1) == -1) {
				syslog(LOG_ERR, "can't set multicast time-to-live \"%s\"", strerror(errno));
				close(t->sock);
				t->sock = -1;
				return(2);
			}
			if(setsockopt(t->sock, IPPROTO_IP, IP_TTL,
				      &ittl, sizeof(ittl)) == -1) {
				syslog(LOG_ERR, "can't set unicast time-to-live \"%s\"", strerror(errno));
				close(t->sock);
				t->sock = -1;
				return(2);
			}
		}
	}

	/* LS - SO_BINDTODEVICE fails on virtual devices, so we skip this if the device name contains a ':' character */
	/* we will bind to the source address in probe_src_ip_addr */
	if(pf == AF_INET && cur->device && *cur->device && !strchr(cur->device,':')) {
		if(setsockopt(t->sock, SOL_SOCKET, SO_BINDTODEVICE, cur->device, strlen(cur->device) + 1) == -1) {
			syslog(LOG_INFO, "failed to bind to ping interface device \"%s\", \"%s\"", cur->device, strerror(errno));
			close(t->sock);
			t->sock = -1;
			return(2);
		}
	}

#if defined(DEBUG)
	if(cfg.debug >= 9) syslog(LOG_INFO, "%s: %s: probing for src ip for %s", __FILE__, __FUNCTION__, cur->name);
#endif
	if(probe_src_ip_addr(cur) != 0) {
		close(t->sock);
		t->sock = -1;
		return(2);
	}
#if defined(DEBUG)
	if(cfg.debug >= 9) syslog(LOG_INFO, "%s: %s: probing for src ip for %s done", __FILE__, __FUNCTION__, cur->name);
#endif
	if(cur->sourceip && *cur->sourceip) {
		if(cur->srcinfo->ai_family == AF_INET) {
			struct sockaddr_in addr;

			memset(&addr, 0, sizeof(addr));
			addr.sin_family = AF_INET;
			addr.sin_addr.s_addr = inet_addr(cur->sourceip);

			if(bind(t->sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
				syslog(LOG_ERR, "ping can't bind \"%s\"", strerror(errno));
				return(1);
			}
		} else {
			struct sockaddr_in6 addr;
#if defined(DEBUG)
			if(cfg.debug >= 9) syslog(LOG_INFO, "%s: %s: setting v6 src addr", __FILE__, __FUNCTION__);
#endif
			memset(&addr, 0, sizeof(addr));
			addr.sin6_family = AF_INET6;
			if(inet_pton(AF_INET6, cur->sourceip, &addr.sin6_addr) != 1) {
				syslog(LOG_ERR, "ping6 failed to convert connection %s address %s", cur->name, cur->sourceip);
				return(1);
			}
			if(bind(t->sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
				syslog(LOG_ERR, "ping6 can't bind %s to %s, \"%s\"", cur->name, cur->sourceip, strerror(errno));
				return(1);
			}
#if defined(DEBUG)
			if(cfg.debug >= 9) syslog(LOG_INFO, "%s: %s: setting v6 src addr done", __FILE__, __FUNCTION__);
#endif
		}
	}

	if(pf == AF_INET6 && cur->device && *cur->device) {
		struct ifreq ifr;
		struct cmsghdr *cmsg;
		struct in6_pktinfo *ipi;

		memset(&ifr, 0, sizeof(ifr));
		strncpy(ifr.ifr_name, cur->device, IFNAMSIZ-1);
		if(ioctl(t->sock, SIOCGIFINDEX, &ifr) < 0) {
			syslog(LOG_ERR, "ping6 unknown iface %s", cur->device);
			return(2);
		}

		memset(&t->cmsgbuf, 0, sizeof(t->cmsgbuf));
		t->cmsglen = 0;

		cmsg = (struct cmsghdr *)t->cmsgbuf;
		t->cmsglen += CMSG_SPACE(sizeof(*ipi));
		cmsg->cmsg_len = CMSG_LEN(sizeof(*ipi));
		cmsg->cmsg_level = SOL_IPV6;
		cmsg->cmsg_type = IPV6_PKTINFO;

		ipi = (struct in6_pktinfo *)CMSG_DATA(cmsg);
		memset(ipi, 0, sizeof(*ipi));
		ipi->ipi6_ifindex = ifr.ifr_ifindex;
	}

	return(0);
}

static int probe_src_ip_addr(CONFIG *cur)
{ /* probe for src ip address */
	TARGET *t = (TARGET *)cur->data;
	int probe_fd;
	int pf = cur->dstinfo->ai_family;

	probe_fd = socket(pf, SOCK_DGRAM, 0);

	if(probe_fd < 0) {
		syslog(LOG_ERR, "ping probe socket for %s failed \"%s\"", cur->name, strerror(errno));
		return(2);
	}
	if(fcntl(t->sock, F_SETFD, FD_CLOEXEC) == -1) {
		syslog(LOG_ERR, "ping probe failed to set close on exec on probe socket for %s reason \"%s\"", cur->name, strerror(errno));
	}

	if(cur->device && *cur->device && !strchr(cur->device,':')) {
		if(setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, cur->device, strlen(cur->device) + 1) == -1)
			syslog(LOG_INFO, "WARNING: ping probe interface \"%s\" is ignored for %s reason \"%s\"", cur->device, cur->name, strerror(errno));
	}

	if(pf == AF_INET) {
		struct sockaddr_in saddr;
		memset(&saddr, 0, sizeof(saddr));
		saddr.sin_family = AF_INET;
		if(t->src.s_addr) {
			saddr.sin_addr = t->src;
			if(bind(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
				syslog(LOG_ERR, "ping probe bind failed for %s \"%s\"", cur->name, strerror(errno));
				close(probe_fd);
				/* earlier probed src addr is not usable, wipe it */
				memset(&t->src, 0, sizeof(t->src));
				return(2);
			}
		} else {
		  /* LS -- modified original logic to use SIOCGIFADDR ioctl to get interface address instead of searching routing table */
                  struct ifreq ifr;
                  bzero((void*)&ifr,sizeof(struct ifreq));
                  strncpy(ifr.ifr_name,cur->device,IFNAMSIZ-1);
                  ifr.ifr_addr.sa_family = pf;
                  if (ioctl(probe_fd,SIOCGIFADDR,&ifr)) {
                    syslog(LOG_ERR,"ioctl probe of current ip address for device %s failed \"%s\"",cur->device,strerror(errno));
                    return(2);
                  }
                  t->src = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr;
                }
		/*
		  else {
			int on = 1;
			int alen = sizeof(saddr);

			saddr.sin_port = htons(1025);
			saddr.sin_addr = t->dst;

			if(setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, (char*)&on, sizeof(on)) == -1)
				syslog(LOG_INFO, "WARNING: ping probe setsockopt(SO_DONTROUTE) \"%s\"", strerror(errno));
			if(connect(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
				syslog(LOG_ERR, "ping probe connect for %s failed \"%s\"", cur->name, strerror(errno));
				close(probe_fd);
				return(2);
			}
			if(getsockname(probe_fd, (struct sockaddr*)&saddr, (socklen_t*)&alen) == -1) {
				syslog(LOG_ERR, "ping probe getsockname for %s failed \"%s\"", cur->name, strerror(errno));
				close(probe_fd);
				return(2);
			}
			t->src = saddr.sin_addr;
		}
		*/
	} else if (pf == AF_INET6) { /* not AF_INET */
		struct sockaddr_in6 saddr;
		unsigned char nulladdr[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

		memset(&saddr, 0, sizeof(saddr));
		saddr.sin6_family = AF_INET6;
		if(memcmp(&t->src6, nulladdr, sizeof(t->src6)) != 0) { /* is not null addr */
			memcpy(&saddr.sin6_addr, &t->src6, sizeof(t->src6));
			if(bind(probe_fd, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
				syslog(LOG_ERR, "ping6 probe bind failed for %s \"%s\"", cur->name,strerror(errno));
				close(probe_fd);
				/* earlier probed src addr is not usable, wipe it */
				memset(&t->src6, 0, sizeof(t->src6));
				return(2);
			}
		} else { /* is null addr */
			socklen_t alen = sizeof(saddr);

			saddr.sin6_port = htons(1025);
			saddr.sin6_family = cur->dstinfo->ai_family;
			memcpy(&saddr.sin6_addr, &t->dst6, sizeof(t->dst6));
#if 0
			if(setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, (char *)&on, sizeof(on)) == -1)
				syslog(LOG_INFO, "WARNING: ping6 probe setsockopt(SO_DONTROUTE) for %s \"%s\"", cur->name, strerror(errno));
#endif
			if(connect(probe_fd, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
				syslog(LOG_ERR, "ping6 probe connect for %s failed \"%s\"", cur->name, strerror(errno));
				close(probe_fd);
				return(2);
			}
			if(getsockname(probe_fd, (struct sockaddr *)&saddr, &alen) == -1) {
				syslog(LOG_ERR, "ping6 probe getsockname for %s failed \"%s\"", cur->name, strerror(errno));
				close(probe_fd);
				return(2);
			}
			memcpy(&t->src6, &saddr.sin6_addr, sizeof(saddr.sin6_addr));
		}
	} /* if AF_INET */

	close(probe_fd);

	return(0);
}

#if defined(DEBUG)
static void dump_pkt(const void *buf, size_t len)
{
	int i;
	unsigned char *s;
	char obuf[BUFSIZ];
	char *pad;

	memset(obuf, 0, BUFSIZ);

	s = (unsigned char *)buf;
	pad = "";
	for(i = 0; i < len && i < BUFSIZ; i++) {
		snprintf(obuf + strlen(obuf), BUFSIZ, "%s%02x", pad, s[i]);
		pad = " ";
	}

	syslog(LOG_INFO, "%s: %s: hexdump %s", __FILE__, __FUNCTION__, obuf);

	memset(obuf, 0, BUFSIZ);

	s = (unsigned char *)buf;
	pad = "";
	for(i = 0; i < len && i < BUFSIZ; i++) {
		snprintf(obuf + strlen(obuf), BUFSIZ, "%s%03d", pad, s[i]);
		pad = " ";
	}

	syslog(LOG_INFO, "%s: %s: decdump %s", __FILE__, __FUNCTION__, obuf);
}
#endif

/* EOF */


<|start_filename|>lsm/foolsm.h<|end_filename|>
/*

(C) 2013 <NAME> <<EMAIL>>

License: GPLv2

*/

#ifndef __FOOLSM_H__
#define __FOOLSM_H__

#include <netinet/in.h> /* for struct sockaddr_in */
#include <linux/if_arp.h> /* for struct sockadd_ll */
#include <netinet/icmp6.h> /* for struct icmp6_filter */

#include "defs.h"

typedef struct sentpkt {
	unsigned short seq;
	struct timeval sent_time;
	struct timeval replied_time;
	unsigned long rtt;
	struct {
		unsigned replied:1;
		unsigned timeout:1;
		unsigned waiting:1;
		unsigned used:1;
		unsigned error:1;
	} flags;
} SENTPKT;

typedef struct target {
	unsigned short id; /* target id */
	unsigned short seq;
	unsigned short downseq;
	unsigned short downseqreported;
	struct timeval down_timestamp;
	struct sockaddr_in src_addr;
	struct sockaddr_in dst_addr;
	struct sockaddr_in6 src_addr6;
	struct sockaddr_in6 dst_addr6;
	struct sockaddr_ll me; /* arping only */
	struct sockaddr_ll he; /* arping only */
	struct in_addr src;
	struct in_addr dst;
	struct in6_addr src6;
	struct in6_addr dst6;
	unsigned long num_sent;
	struct timeval last_send_time;
	STATUS status;
	int sock;
	unsigned char cmsgbuf[4096];
	int cmsglen;
	struct icmp6_filter filter;
	SENTPKT sentpkts[FOLLOWED_PKTS];
	int timeout;
	int timeout_max;
	int replied;
	int waiting;
	int reply_late;
	int used;
	int consecutive_waiting;
	int consecutive_missing;
	int consecutive_missing_max;
	int consecutive_rcvd;
	long avg_rtt;
	int status_change;
} TARGET;

#endif

/* EOF */


<|start_filename|>lsm/config.c<|end_filename|>
/*

(C) 2009-2019 <NAME> <<EMAIL>>

License: GPLv2

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <dirent.h>
#include <fnmatch.h>

#include "config.h"
#include "defs.h"

#define DEFAULT_SCRIPT_FILE SCRIPTDIR "/default_script"

static CONFIG defaults;
static int errors = 0;

GLOBAL cfg;

static void read_one_config(char *fn, CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg);
static int find_all_configs(char* fn, int mustexist, CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg);
static void reassign(char **dst, char *src);
static void release(char **dst);
static int eqcmp(char *str, char *pat);
static int check_addrs(CONFIG *cur);

static void reassign(char **dst, char *src)
{
	if(*dst) free(*dst);
	*dst = strdup(src);
}

static void release(char **dst)
{
	if(*dst) free(*dst);
	*dst = NULL;
}

static int eqcmp(char *str, char *pat)
{
	int i;

	i = strlen(pat);
	if(!strncmp(str, pat, i) && str[i] == '=') return 0;
	return 1;
}

int reload_config(char *fn, CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg) {
	free_config(first, last, firstg, lastg);
	init_config();
	return(read_config(fn, first, last, firstg, lastg));
}

void free_config(CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg) {
	CONFIG *cur, *prev;
	GROUPS *curg, *prevg;
	GROUP_MEMBERS *curgm, *prevgm;

	cur = (*first);
	while(cur) {
		if(cur->name && cur->name != defaults.name)                            release(&cur->name);
		if(cur->sourceip && cur->sourceip != defaults.sourceip)                release(&cur->sourceip);
		if(cur->srcinfo)                                                       freeaddrinfo(cur->srcinfo);
		if(cur->checkip && cur->checkip != defaults.checkip)                   release(&cur->checkip);
		if(cur->dstinfo)                                                       freeaddrinfo(cur->dstinfo);
		if(cur->eventscript && cur->eventscript != defaults.eventscript)       release(&cur->eventscript);
		if(cur->notifyscript && cur->notifyscript != defaults.notifyscript)    release(&cur->notifyscript);
		if(cur->warn_email && cur->warn_email != defaults.warn_email)          release(&cur->warn_email);
		if(cur->device && cur->device != defaults.device)                      release(&cur->device);
		if(cur->queue && cur->queue != defaults.queue)                         release(&cur->queue);
		if(cur->long_down_email && cur->long_down_email != defaults.long_down_email) release(&cur->long_down_email);
		if(cur->long_down_notifyscript && cur->long_down_notifyscript != defaults.long_down_notifyscript) release(&cur->long_down_notifyscript);
		if(cur->long_down_eventscript && cur->long_down_eventscript != defaults.long_down_eventscript) release(&cur->long_down_eventscript);

		prev = cur;
		cur = cur->next;
		free(prev);
	}

	*first = NULL;
	*last = NULL;

	curg = (*firstg);
	while(curg) {
		curgm = curg->fgm;
		while(curgm) {
			free(curgm->name);

			prevgm = curgm;
			curgm = curgm->next;
			free(prevgm);
		}
		curg->fgm = NULL;
		curg->lgm = NULL;

		if(curg->name && curg->name != defaults.name)				release(&curg->name);
		if(curg->eventscript && curg->eventscript != defaults.eventscript)	release(&curg->eventscript);
		if(curg->notifyscript && curg->notifyscript != defaults.notifyscript)	release(&curg->notifyscript);
		if(curg->warn_email && curg->warn_email != defaults.warn_email)		release(&curg->warn_email);
		if(curg->device && curg->device != defaults.device)                     release(&curg->device);
		if(curg->queue && curg->queue != defaults.queue)                        release(&curg->queue);

		prevg = curg;
		curg = curg->next;
		free(prevg);
	}

	*firstg = NULL;
	*lastg = NULL;

	if(defaults.name)                   release(&defaults.name);
	if(defaults.checkip)                release(&defaults.checkip);
	if(defaults.eventscript)            release(&defaults.eventscript);
	if(defaults.notifyscript)           release(&defaults.notifyscript);
	if(defaults.warn_email)             release(&defaults.warn_email);
	if(defaults.sourceip)               release(&defaults.sourceip);
	if(defaults.device)                 release(&defaults.device);
	if(defaults.queue)                  release(&defaults.queue);

	if(defaults.long_down_email)        release(&defaults.long_down_email);
	if(defaults.long_down_notifyscript) release(&defaults.long_down_notifyscript);
	if(defaults.long_down_eventscript)  release(&defaults.long_down_eventscript);
}

void init_config(void)
{
	/* zero cfg and defaults */
	memset(&cfg, 0, sizeof(cfg));
	memset(&defaults, 0, sizeof(defaults));

	/* initialize to sane value */
	cfg.debug = 8;

	defaults.name = strdup("defaults");
	defaults.checkip = strdup("127.0.0.1");
	defaults.eventscript = NULL;
	defaults.notifyscript = strdup(DEFAULT_SCRIPT_FILE);
	defaults.max_packet_loss = 15;
	defaults.max_successive_pkts_lost = 7;
	defaults.min_packet_loss = 5;
	defaults.min_successive_pkts_rcvd = 10;
	defaults.interval_ms = 1000;
	defaults.timeout_ms = 1000;
	defaults.warn_email = strdup("root");
	defaults.check_arp = 0;
	defaults.sourceip = NULL;
	defaults.ttl = 0;

	/* assume default unknown state for connections unless user has stated otherwise later in config */
	defaults.status = UNKNOWN;

	/* no exec queue by default */
	defaults.queue = NULL;

	defaults.long_down_email = NULL;

	/* by default don't execute notify script on unkown to up event */
	defaults.unknown_up_notify = 0;

	/* by default no accelerated startup */
	defaults.startup_acceleration = 0;

	/* by default no startup burst */
	defaults.startup_burst_pkts = 0;
	defaults.startup_burst_interval = MIN_PERHOST_INTERVAL;
}

static int find_all_configs(char* fn, int mustexist, CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg)
{
	struct dirent **namelist;
	char dir[BUFSIZ], pattern[128], *p, s[BUFSIZ];
	int n, i, found;

	/* Split fn to dir/pattern */
	strcpy(dir, fn);
	if((p = strrchr(dir, '/')) == NULL) {
		strcpy(dir, ".");
		strcpy(pattern, fn);
	} else {
		*p = 0;
		strcpy(pattern, p + 1);
	}

	/* Find list of all files */
	n = scandir(dir, &namelist, 0, alphasort);
	if (n < 0) {
		if (mustexist == 0)
			return(0);
		syslog(LOG_ERR, "%s: can't read directory \"%s\"", __FUNCTION__, dir);
		return(-1);
	}

	/* See if name matches pattern */
	found = 0;
	for (i = 0; i < n; i++) {
		if (fnmatch(pattern, namelist[i]->d_name, 0) == 0 &&
		    fnmatch("*~", namelist[i]->d_name, 0) != 0) {
			snprintf(s, BUFSIZ, "%s/%s", dir, namelist[i]->d_name);
			read_one_config(s, first, last, firstg, lastg);
			found++;
		}
		free(namelist[i]);
	}
	free(namelist);

	/* Fail if no matches found */
	if (found == 0) {
		if (mustexist == 0)
			return(0);
		syslog(LOG_ERR, "%s: no config files found for \"%s\"", __FUNCTION__, fn);
		return(-1);
	}

	return(0);
}

int read_config(char *fn, CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg)
{
	CONFIG *cur = NULL;
	GROUPS *curg = NULL;
	GROUP_MEMBERS *curgm = NULL;

	errors = 0;

	read_one_config(fn, first, last, firstg, lastg);

	for(curg = *firstg; curg; curg = curg->next) {
		for(curgm = curg->fgm; curgm; curgm = curgm->next) {
			int found = 0;

			for(cur = *first; cur; cur = cur->next) {
				if(!strcmp(cur->name, curgm->name)) {
					curgm->cfg_ptr = cur;
					found = 1;
					break;
				}
			}
			if(!found) {
				syslog(LOG_ERR, "%s: %s: connection group member \"%s\" not found", __FILE__, __FUNCTION__, curgm->name);
				errors++;
			}
		}
	}

	/* some parameter sanity checking */
	for(cur = *first; cur; cur = cur->next) {
		if(strlen(cur->checkip) == 0) {
			syslog(LOG_ERR, "WARNING: connection \"%s\" has no checkip parameter set", cur->name);
			errors++;
		} else {
			if(check_addrs(cur) < 0) {
				errors++;
			}
		}

		if(cur->max_packet_loss <= cur->min_packet_loss) {
			syslog(LOG_ERR, "WARNING: connection \"%s\" max_packet_loss (%d) <= min_packet_loss (%d). that would cause flip-flop effect", cur->name, cur->max_packet_loss, cur->min_packet_loss);
			errors++;
		}

	}
	if(errors) return(-1);

	return(0);
}

static void read_one_config(char *fn, CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg)
{
	CONFIG *cur = NULL;
	GROUPS *curg = NULL;
	GROUP_MEMBERS *curgm = NULL;
	FILE *fp;
	char buf[BUFSIZ];
	int mode = 0;
	int line = 1;

	if((fp = fopen(fn, "r")) == 0) {
		syslog(LOG_ERR, "%s: can't open config file \"%s\"", __FUNCTION__, fn);
		return;
	}

	while(fgets(buf, BUFSIZ, fp)) {
		char *p = NULL;

		if(*buf && buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = '\0'; /* strip lf */
		if((p = strchr(buf, '#')) != NULL) *p = '\0'; /* strip comment */
		while((p = strchr(buf, '\t')) != NULL) *p = ' '; /* tabs -> spaces */
		while(*buf == ' ') memmove(buf, buf + 1, strlen(buf)); /* strip leading space */
		while((p = strstr(buf, "  ")) != NULL) memmove(p, p + 1, strlen(p)); /* strip multi white space */
		while((p = strstr(buf, " =")) != NULL) memmove(p, p + 1, strlen(p)); /* strip spaces before = */
		while((p = strstr(buf, "= ")) != NULL) memmove(p + 1, p + 2, strlen(p + 1)); /* strip spaces after = */
		while(*buf && buf[strlen(buf) - 1] == ' ') buf[strlen(buf) - 1] = '\0'; /* strip tailing space */

		if(!*buf) continue;

		if(mode) {
			if (!strcmp(buf, "}")) {
				mode=0;
				continue;
			}

			switch(mode) {
			case 1: /* defaults */
				if(!eqcmp(buf, "name"))
					reassign(&defaults.name, strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "checkip"))
					reassign(&defaults.checkip, strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "eventscript"))
					reassign(&defaults.eventscript, strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "notifyscript"))
					reassign(&defaults.notifyscript, strchr(buf, '=') + 1);

				else if(!eqcmp(buf, "unknown_up_notify"))
					defaults.unknown_up_notify = atoi(strchr(buf, '=') + 1);

				else if(!eqcmp(buf, "max_packet_loss"))
					defaults.max_packet_loss = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "max_successive_pkts_lost"))
					defaults.max_successive_pkts_lost = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "min_packet_loss"))
					defaults.min_packet_loss = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "min_successive_pkts_rcvd"))
					defaults.min_successive_pkts_rcvd = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "interval_ms"))
					defaults.interval_ms = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "timeout_ms"))
					defaults.timeout_ms = atoi(strchr(buf, '=') + 1);

				else if(!eqcmp(buf, "warn_email"))
					reassign(&defaults.warn_email, strchr(buf, '=') + 1);

				else if(!eqcmp(buf, "check_arp"))
					defaults.check_arp = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "sourceip"))
					reassign(&defaults.sourceip, strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "device"))
					reassign(&defaults.device, strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "ttl"))
					defaults.ttl = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "status"))
					defaults.status = atoi(strchr(buf, '=') + 1);

				else if(!eqcmp(buf, "queue"))
					reassign(&defaults.queue, strchr(buf, '=') + 1);

				else if(!eqcmp(buf, "long_down_time"))
					defaults.long_down_time = atoi(strchr(buf, '=') + 1);

				else if(!eqcmp(buf, "long_down_email"))
					reassign(&defaults.long_down_email, strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "long_down_notifyscript"))
					reassign(&defaults.long_down_notifyscript, strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "long_down_eventscript"))
					reassign(&defaults.long_down_eventscript, strchr(buf, '=') + 1);

				else if(!eqcmp(buf, "startup_acceleration"))
					defaults.startup_acceleration = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "startup_burst_pkts"))
					defaults.startup_burst_pkts = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "startup_burst_interval"))
					defaults.startup_burst_interval = atoi(strchr(buf, '=') + 1);
				else {
					syslog(LOG_ERR, "%s: %s: unrecognised "
					       "default config option on "
					       "line %d \"%s\"", __FILE__,
					       __FUNCTION__, line, buf);
					errors++;
				}
				break;
			case 2: /* connection */
				if(!cur) {
					syslog(LOG_ERR, "read_config: cur == NULL");
					break;
				}

				if(!eqcmp(buf, "name"))                            cur->name                          = strdup(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "checkip"))                    cur->checkip                       = strdup(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "eventscript"))                cur->eventscript                   = strdup(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "notifyscript"))               cur->notifyscript                  = strdup(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "unknown_up_notify"))          cur->unknown_up_notify             = atoi(strchr(buf, '=') + 1);

				else if(!eqcmp(buf, "max_packet_loss"))            cur->max_packet_loss               = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "max_successive_pkts_lost"))   cur->max_successive_pkts_lost      = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "min_packet_loss"))            cur->min_packet_loss               = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "min_successive_pkts_rcvd"))   cur->min_successive_pkts_rcvd      = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "interval_ms"))                cur->interval_ms                   = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "timeout_ms"))                 cur->timeout_ms                    = atoi(strchr(buf, '=') + 1);

				else if(!eqcmp(buf, "warn_email"))                 cur->warn_email                    = strdup(strchr(buf, '=') + 1);

				else if(!eqcmp(buf, "check_arp"))                  cur->check_arp                     = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "sourceip"))                   cur->sourceip                      = strdup(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "device"))                     cur->device                        = strdup(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "ttl"))                        cur->ttl                           = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "status"))                     cur->status                        = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "queue"))                      cur->queue                         = strdup(strchr(buf, '=') + 1);

				else if(!eqcmp(buf, "long_down_time"))             cur->long_down_time                = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "long_down_email"))            cur->long_down_email               = strdup(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "long_down_notifyscript"))     cur->long_down_notifyscript        = strdup(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "long_down_eventscript"))      cur->long_down_eventscript         = strdup(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "startup_acceleration"))       cur->startup_acceleration          = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "startup_burst_pkts"))         cur->startup_burst_pkts            = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "startup_burst_interval"))     cur->startup_burst_interval        = atoi(strchr(buf, '=') + 1);
				else {
					syslog(LOG_ERR, "%s: %s: unrecognised connection config option on line %d \"%s\"", __FILE__, __FUNCTION__, line, buf);
					errors++;
				}
				break;
			case 3: /* group */
				if(!eqcmp(buf, "name"))                            curg->name				= strdup(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "eventscript"))                curg->eventscript			= strdup(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "notifyscript"))               curg->notifyscript			= strdup(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "unknown_up_notify"))          curg->unknown_up_notify              = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "warn_email"))                 curg->warn_email			= strdup(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "logic"))                      curg->logic				= atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "device"))                     curg->device                         = strdup(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "status"))			   curg->status                         = atoi(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "queue"))                      curg->queue                          = strdup(strchr(buf, '=') + 1);
				else if(!eqcmp(buf, "member-connection")) {
					if((curgm = (GROUP_MEMBERS *)malloc(sizeof(GROUP_MEMBERS))) == NULL) {
						syslog(LOG_ERR, "%s: %s: can't malloc for group member", __FILE__, __FUNCTION__);
						fclose(fp);
						return;
					}
					curgm->name = strdup(strchr(buf, '=') + 1);
					curgm->cfg_ptr = NULL;

					if(curg->lgm) { /* insert as last */
						curgm->next = NULL;
						curgm->prev = curg->lgm;
						curg->lgm->next = curgm;
						curg->lgm = curgm;
					} else { /* empty member list */
						curgm->next = NULL;
						curgm->prev = NULL;
						curg->fgm = curgm;
						curg->lgm = curgm;
					}
				}
				else {
					syslog(LOG_ERR, "%s: %s: unrecognised group config option on line %d \"%s\"", __FILE__, __FUNCTION__, line, buf);
					errors++;
				}
				break;
			default:
				syslog(LOG_ERR, "%s: %s: switch(mode) hit default: should never happen. mode was %d", __FILE__, __FUNCTION__, mode);
				errors++;
				break;
			}
		}
		else {
			/* global config */
			if(!eqcmp(buf, "debug"))
				cfg.debug = atoi(strchr(buf, '=') + 1);

			/* per connection configs */
			else if(!strcmp(buf, "defaults {"))
				mode=1;
			else if(!strcmp(buf, "connection {")) {
				mode=2;
				if((cur = malloc(sizeof(CONFIG))) == NULL) {
					syslog(LOG_ERR, "%s: %s: can't malloc for config", __FILE__, __FUNCTION__);
					return;
				}

				if(*last) { /* not first */
					(*last)->next = cur;
					cur->prev = *last;
					cur->next = NULL;
					*last = cur;
				}
				else {
					*first = cur;
					*last = cur;
					cur->prev = NULL;
					cur->next = NULL;
				}
				/* fill in defaults */
				if(defaults.name) {
					cur->name                       = defaults.name;
					cur->sourceip                   = defaults.sourceip;
					cur->srcinfo                    = NULL;
					cur->checkip                    = defaults.checkip;
					cur->dstinfo                    = NULL;
					cur->eventscript                = defaults.eventscript;
					cur->notifyscript               = defaults.notifyscript;
					cur->unknown_up_notify          = defaults.unknown_up_notify;
					cur->max_packet_loss            = defaults.max_packet_loss;
					cur->max_successive_pkts_lost   = defaults.max_successive_pkts_lost;
					cur->min_packet_loss            = defaults.min_packet_loss;
					cur->min_successive_pkts_rcvd   = defaults.min_successive_pkts_rcvd;
					cur->interval_ms                = defaults.interval_ms;
					cur->timeout_ms                 = defaults.timeout_ms;
					cur->warn_email                 = defaults.warn_email;
					cur->check_arp                  = defaults.check_arp;
					cur->device                     = defaults.device;
					cur->ttl                        = defaults.ttl;
					cur->status			= defaults.status;
					cur->queue                      = defaults.queue;
					cur->long_down_time             = defaults.long_down_time;
					cur->long_down_email            = defaults.long_down_email;
					cur->long_down_notifyscript     = defaults.long_down_notifyscript;
					cur->long_down_eventscript      = defaults.long_down_eventscript;
					cur->startup_acceleration	= defaults.startup_acceleration;
					cur->startup_burst_pkts		= defaults.startup_burst_pkts;
					cur->startup_burst_interval     = defaults.startup_burst_interval;
				}
				else
					syslog(LOG_ERR, "%s: %s: defaults not set", __FILE__, __FUNCTION__);
			}
			else if(!strcmp(buf, "group {")) {
				mode = 3;

				if((curg = (GROUPS *)malloc(sizeof(GROUPS))) == NULL) {
					syslog(LOG_ERR, "read_config: can't malloc for group");
					return;
				}

				/* apply sane defaults for group */
				curg->name            = defaults.name;
				curg->eventscript     = defaults.eventscript;
				curg->notifyscript    = defaults.notifyscript;
				curg->unknown_up_notify = defaults.unknown_up_notify;
				curg->warn_email      = defaults.warn_email;
				curg->logic           = 0; /* default group logic or */
				curg->device          = defaults.device;
				curg->status          = defaults.status;
				curg->queue           = defaults.queue;

				curg->fgm = NULL;
				curg->lgm = NULL;

				if(*lastg) { /* not first group */
					(*lastg)->next = curg;
					curg->prev = *lastg;
					curg->next = NULL;
					*lastg = curg;
				} else {
					*firstg = curg;
					*lastg = curg;
					curg->prev = NULL;
					curg->next = NULL;
				}
			}
			else if(!strncmp(buf, "include ", 8)) {
				if(find_all_configs(strchr(buf, ' ') + 1, 1, first, last, firstg, lastg) != 0) {
					syslog(LOG_ERR, "%s: %s: failed to process included config file on line %d \"%s\"", __FILE__, __FUNCTION__, line, strchr(buf, ' ') + 1);
					errors++;
				}
				continue;
			}
			else if(!strncmp(buf, "-include ", 9)) {
				if(find_all_configs(strchr(buf, ' ') + 1, 0, first, last, firstg, lastg) != 0) {
					syslog(LOG_ERR, "%s: %s: failed to process included config file on line %d \"%s\"", __FILE__, __FUNCTION__, line, strchr(buf, ' ') + 1);
					errors++;
				}
				continue;
			}
			else {
				syslog(LOG_ERR, "%s: %s: unrecognised global config option in file \"%s\" on line %d \"%s\"", __FILE__, __FUNCTION__, fn, line, buf);
				errors++;
			}
		}
		line++;
	}

	if(mode != 0) {
		syslog(LOG_ERR, "%s: %s: missing closing bracket at the end of config file \"%s\"", __FILE__, __FUNCTION__, fn);
		errors++;
	}

	fclose(fp);
}

void dump_config(CONFIG **first, CONFIG **last, GROUPS **firstg, GROUPS **lastg)
{
	CONFIG *cur;
	GROUPS *curg;
	GROUP_MEMBERS *curgm;

	syslog(LOG_INFO,   "cfg.debug                     = \"%d\"", cfg.debug);

	for(cur = *first; cur; cur = cur->next) {
		syslog(LOG_INFO, "cur->name                     = \"%s\"", cur->name);
		syslog(LOG_INFO, "cur->sourceip                 = \"%s\"", cur->sourceip);
#if defined(DEBUG)
		if(cur->srcinfo) {
			char sbuf[INET6_ADDRSTRLEN];

			syslog(LOG_INFO, "cur->srcinfo                  = \"%s\"", inet_ntop(cur->srcinfo->ai_family, &cur->srcinfo->ai_addr, sbuf, INET6_ADDRSTRLEN));
		}
#endif
		syslog(LOG_INFO, "cur->checkip                  = \"%s\"", cur->checkip);
#if defined(DEBUG)
		if(cur->dstinfo) {
			char sbuf[INET6_ADDRSTRLEN];

			syslog(LOG_INFO, "cur->dstinfo                  = \"%s\"", inet_ntop(cur->dstinfo->ai_family, &cur->dstinfo->ai_addr, sbuf, INET6_ADDRSTRLEN));
		}
#endif
		syslog(LOG_INFO, "cur->eventscript              = \"%s\"", cur->eventscript);
		syslog(LOG_INFO, "cur->notifyscript             = \"%s\"", cur->notifyscript);
		syslog(LOG_INFO, "cur->unknown_up_notify        = \"%d\"", cur->unknown_up_notify);

		syslog(LOG_INFO, "cur->max_packet_loss          = \"%d\"", cur->max_packet_loss);
		syslog(LOG_INFO, "cur->max_successive_pkts_lost = \"%d\"", cur->max_successive_pkts_lost);
		syslog(LOG_INFO, "cur->min_packet_loss          = \"%d\"", cur->min_packet_loss);
		syslog(LOG_INFO, "cur->min_successive_pkts_rcvd = \"%d\"", cur->min_successive_pkts_rcvd);
		syslog(LOG_INFO, "cur->interval_ms              = \"%d\"", cur->interval_ms);
		syslog(LOG_INFO, "cur->timeout_ms               = \"%d\"", cur->timeout_ms);

		syslog(LOG_INFO, "cur->warn_email               = \"%s\"", cur->warn_email);

		syslog(LOG_INFO, "cur->check_arp                = \"%d\"", cur->check_arp);
		syslog(LOG_INFO, "cur->device                   = \"%s\"", cur->device);
		syslog(LOG_INFO, "cur->ttl                      = \"%d\"", cur->ttl);
		syslog(LOG_INFO, "cur->status                   = \"%d\"", cur->status);
		syslog(LOG_INFO, "cur->startup_acceleration     = \"%d\"", cur->startup_acceleration);
		syslog(LOG_INFO, "cur->startup_burst_pkts       = \"%d\"", cur->startup_burst_pkts);
		syslog(LOG_INFO, "cur->startup_burst_interval   = \"%d\"", cur->startup_burst_interval);
	}

	for(curg = *firstg; curg; curg = curg->next) {
		syslog(LOG_INFO, "curg->name                    = \"%s\"", curg->name);
		syslog(LOG_INFO, "curg->eventscript             = \"%s\"", curg->eventscript);
		syslog(LOG_INFO, "curg->notifyscript            = \"%s\"", curg->notifyscript);
		syslog(LOG_INFO, "curg->unknown_up_notify       = \"%d\"", curg->unknown_up_notify);
		syslog(LOG_INFO, "curg->warn_email              = \"%s\"", curg->warn_email);
		syslog(LOG_INFO, "curg->device                  = \"%s\"", curg->device);
		syslog(LOG_INFO, "curg->logic                   = \"%s\"", curg->logic == 0 ? "OR" : "AND");

		for(curgm = curg->fgm; curgm; curgm = curgm->next) {
			syslog(LOG_INFO, "curgm->name                   = \"%s\"", curgm->name);
		}
	}
}

static int check_addrs(CONFIG *cur)
{
	struct in6_addr serveraddr;
	struct addrinfo hints;
#if defined(DEBUG)
	struct addrinfo *rp;
#endif
	int rc;

	memset(&hints, 0, sizeof(hints));
	hints.ai_flags    = AI_NUMERICSERV;
	hints.ai_family   = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;

	if((rc = inet_pton(AF_INET, cur->checkip, &serveraddr)) == 1) { /* valid v4 addr */
		hints.ai_family = AF_INET;
		hints.ai_flags |= AI_NUMERICHOST;
	}
	else if((rc = inet_pton(AF_INET6, cur->checkip, &serveraddr)) == 1) { /* valid v6 addr */
		hints.ai_family = AF_INET6;
		hints.ai_flags |= AI_NUMERICHOST;
	}

	if((rc = getaddrinfo(cur->checkip, "1025", &hints, &(cur->dstinfo))) != 0) {
		syslog(LOG_ERR, "WARNING: connection \"%s\" checkip is invalid %s, %s", cur->name, cur->checkip, gai_strerror(rc));
		return(-1);
	}

#if defined(DEBUG)
	for(rp = cur->dstinfo; rp; rp = rp->ai_next) {
		unsigned char *s;
		char buf[BUFSIZ];
		char sbuf[INET6_ADDRSTRLEN];
		int i;

		memset(buf, 0, BUFSIZ);
		s = (unsigned char *)rp;

		strcat(buf, "hex dump:");
		for(i = 0; i < sizeof(struct addrinfo); i++)
			sprintf(buf + strlen(buf), " %2x", s[i]);
		syslog(LOG_INFO, "%s: %s: dst %s", __FILE__, __FUNCTION__, buf);

		memset(buf, 0, BUFSIZ);
		s = (unsigned char *)rp;

		strcat(buf, "dec dump:");
		for(i = 0; i < sizeof(struct addrinfo); i++)
			sprintf(buf + strlen(buf), " %3d", s[i]);
		syslog(LOG_INFO, "%s: %s: dst %s", __FILE__, __FUNCTION__, buf);

		syslog(LOG_INFO, "%s: %s: dst %s = %s", __FILE__, __FUNCTION__, cur->checkip, inet_ntop(rp->ai_family, &rp->ai_addr, sbuf, INET6_ADDRSTRLEN));
	}
#endif

	if(cur->dstinfo->ai_family == AF_INET6 && cur->check_arp) {
		syslog(LOG_ERR, "WARNING: connection \"%s\" ipv6 and arping are not compatible", cur->name);
		return(-1);
	}

	if(!cur->sourceip || !*cur->sourceip) return(0); /* sourceip is not mandatory */

	memset(&hints, 0, sizeof(hints));
	hints.ai_flags    = AI_NUMERICSERV;
	hints.ai_family   = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;

	if((rc = inet_pton(AF_INET, cur->sourceip, &serveraddr)) == 1) { /* valid v4 addr */
		hints.ai_family = AF_INET;
		hints.ai_flags |= AI_NUMERICHOST;
	}
	else if((rc = inet_pton(AF_INET6, cur->sourceip, &serveraddr)) == 1) { /* valid v6 addr */
		hints.ai_family = AF_INET6;
		hints.ai_flags |= AI_NUMERICHOST;
	}

	if((rc = getaddrinfo(cur->sourceip, "1025", &hints, &(cur->srcinfo))) != 0) {
		syslog(LOG_ERR, "WARNING: connection \"%s\" sourceip is invalid %s, %s", cur->name, cur->sourceip, gai_strerror(rc));
		return(-1);
	}

#if defined(DEBUG)
	for(rp = cur->srcinfo; rp; rp = rp->ai_next) {
		unsigned char *s;
		char buf[BUFSIZ];
		char sbuf[INET6_ADDRSTRLEN];
		int i;

		memset(buf, 0, BUFSIZ);
		s = (unsigned char *)rp;

		strcat(buf, "hex dump:");
		for(i = 0; i < sizeof(struct addrinfo); i++)
			sprintf(buf + strlen(buf), " %2x", s[i]);
		syslog(LOG_INFO, "%s: %s: src %s", __FILE__, __FUNCTION__, buf);

		memset(buf, 0, BUFSIZ);
		s = (unsigned char *)rp;

		strcat(buf, "dec dump:");
		for(i = 0; i < sizeof(struct addrinfo); i++)
			sprintf(buf + strlen(buf), " %3d", s[i]);
		syslog(LOG_INFO, "%s: %s: src %s", __FILE__, __FUNCTION__, buf);

		syslog(LOG_INFO, "%s: %s: src %s = %s", __FILE__, __FUNCTION__, cur->checkip, inet_ntop(rp->ai_family, &rp->ai_addr, sbuf, INET6_ADDRSTRLEN));
	}
#endif

	if(cur->srcinfo->ai_family != cur->dstinfo->ai_family) {
		syslog(LOG_ERR, "WARNING: connection \"%s\" sourceip and checkip have unmatching protocol families", cur->name);
		return(-1);
	}

	return(0);
}

/* EOF */


<|start_filename|>lsm/globals.h<|end_filename|>
/*

(C) 2009-2011 <NAME> <<EMAIL>>

License: GPLv2

*/

#ifndef __GLOBALS_H__
#define __GLOBALS_H__

#include "config.h"

void set_prog(char *val);
char *get_prog(void);
void set_cont(const int val);
int get_cont(void);
void set_dump(const int val);
int get_dump(void);
void set_ident(const int val);
int get_ident(void);
void set_reload_cfg(const int val);
int get_reload_cfg(void);
void set_dump_if_list(const int val);
int get_dump_if_list(void);
void set_configfile(char *val);
char *get_configfile(void);
void set_pidfile(char *val);
char *get_pidfile(void);
void set_nodaemon(const int val);
int get_nodaemon(void);
char *get_status_str(STATUS val);

#endif

/* EOF */


<|start_filename|>lsm/cmdline.h<|end_filename|>
/*

  (C) 2014 <NAME> <<EMAIL>>

  License: GPLv2

*/

#ifndef __CMDLINE_H__
#define __CMDLINE_H__

void cmdline_parse(int argc, char *argv[]);

#endif

/* EOF */


<|start_filename|>lsm/pidfile.h<|end_filename|>
/*

  (C) 2014 <NAME> <<EMAIL>>

  License: GPLv2

*/

#ifndef __PIDFILE_H__
#define __PIDFILE_H__

int pidfile_open(void);
int pidfile_update(void);
void pidfile_close(void);

#endif

/* EOF */


<|start_filename|>lsm/usage.c<|end_filename|>
/*

  (C) 2014 <NAME> <<EMAIL>>

  License: GPLv2

*/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

#include "globals.h"
#include "usage.h"

void usage_and_exit(void) {
#if defined(FOOLSM_VERSION)
        printf("%s version %s\n", get_prog(), FOOLSM_VERSION);
#endif
	printf("usage: %s\n"
	       "       [-h|--help|-v|--version]\n"
	       "       [-c|--config <config_file>]\n"
	       "       [-p|--pidfile <pid_file>]\n"
	       "       [-f|--no-fork]\n", get_prog());
	printf("check syslog for debug/error messages\n");

	exit(2);
}

/* EOF */


<|start_filename|>lsm/globals.c<|end_filename|>
/*

(C) 2009-2011 <NAME> <<EMAIL>>

License: GPLv2

*/

#include <stdio.h>
#include <syslog.h>

#include "globals.h"
#include "defs.h"

#define FOOLSM_CONFIG_FILE ETCDIR "/foolsm.conf"

static char *prog = NULL;
static int cont = TRUE;
static int dump = FALSE;
static int ident = 0;
static int reload_cfg = FALSE;
static int dump_if_list = FALSE;
static char *configfile = FOOLSM_CONFIG_FILE;
static char *pidfile = "/var/run/foolsm.pid";
static int nodaemon = 0;
static char *status_str[] = { "down", "up", "unknown", "long_down" };

void set_prog(char *val)
{
	prog = val;
}

char *get_prog(void)
{
	if(prog == NULL) {
		syslog(LOG_ERR, "%s: called with prog unset", __FUNCTION__);
		return("prog unset");
	}

	return(prog);
}

void set_cont(const int val)
{
	cont = val;
}

int get_cont(void)
{
	return(cont);
}

void set_dump(const int val)
{
	dump = val;
}

int get_dump(void)
{
	return(dump);
}

void set_ident(const int val)
{
	ident = val;
}

int get_ident(void)
{
	return(ident);
}

void set_reload_cfg(const int val)
{
	reload_cfg = val;
}

int get_reload_cfg(void)
{
	return(reload_cfg);
}

void set_dump_if_list(const int val)
{
	dump_if_list = val;
}

int get_dump_if_list(void)
{
	return(dump_if_list);
}

void set_configfile(char *val)
{
	configfile = val;
}

char *get_configfile(void)
{
	return(configfile);
}

void set_pidfile(char *val)
{
	pidfile = val;
}

char *get_pidfile(void)
{
	return(pidfile);
}

void set_nodaemon(const int val)
{
	nodaemon = val;
}

int get_nodaemon(void)
{
	return(nodaemon);
}

char *get_status_str(STATUS val)
{
	return(status_str[val]);
}

/* EOF */


<|start_filename|>lsm/cmdline.c<|end_filename|>
/*

  (C) 2014 <NAME> <<EMAIL>>

  License: GPLv2

*/

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>

#include "globals.h"
#include "usage.h"
#include "cmdline.h"

void cmdline_parse(int argc, char *argv[])
{
	static struct option long_options[] = {
		{ "help",               0, 0, 'h' },
		{ "version",            0, 0, 'v' },
		{ "config",             1, 0, 'c' },
		{ "pidfile",            1, 0, 'p' },
		{ "no-fork",            0, 0, 'f' },
		{ 0, 0, 0, 0 },
	};

	set_prog(argv[0]);

	for(;;) {
		int i = getopt_long(argc, argv, "hvc:p:f", long_options, NULL);
		if(i == -1) break;
		switch(i) {
		case 'h':
		case 'v':
			usage_and_exit();

		case 'c':
			set_configfile(optarg);
			break;

		case 'p':
			set_pidfile(optarg);
			break;

		case 'f':
			set_nodaemon(1);
			break;

		default:
			usage_and_exit();
		}
	}

	if(optind < argc)
		usage_and_exit();
}

/* EOF */


<|start_filename|>lsm/pidfile.c<|end_filename|>
/*

  (C) 2014 <NAME> <<EMAIL>>

  License: GPLv2

 */

#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <unistd.h>
#include <string.h>

#include "pidfile.h"
#include "globals.h"

static int pidfilefh = 0;

int pidfile_open(void)
{
	if(get_nodaemon()) return(0);

	pidfilefh = open(get_pidfile(), O_RDWR|O_CREAT, 0640);

	if(pidfilefh < 0) {
		syslog(LOG_ERR, "can't open pid file %s", get_pidfile());
		return(1); /* can not open */
	}
	if(fcntl(pidfilefh, F_SETFD, FD_CLOEXEC) == -1) {
		syslog(LOG_ERR, "failed to set close on exec on pid file %s", get_pidfile());
	}

	if(lockf(pidfilefh, F_TLOCK, 0) < 0) {
		syslog(LOG_ERR, "can't lock pid file %s", get_pidfile());
		return(1); /* can not lock */
	}

	return(0);
}

int pidfile_update(void)
{
	if(get_nodaemon()) return(0);

	if(pidfilefh) {
		char str[BUFSIZ];
		ssize_t n;

		lseek(pidfilefh, 0, SEEK_SET);
		if(ftruncate(pidfilefh, 0) == -1) {
			syslog(LOG_ERR, "ftruncate failed \"%s\"", strerror(errno));
			return(1);
		}

		sprintf(str, "%d\n", getpid());
		n = write(pidfilefh, str, strlen(str)); /* record pid to lockfile */
		if(n == -1) {
			syslog(LOG_ERR, "write failed \"%s\"", strerror(errno));
			return(1);
		}
		if(n != strlen(str)) {
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__amd64__)
			syslog(LOG_ERR, "write failed, %ld bytes written of %ld bytes", n, strlen(str));
#else
			syslog(LOG_ERR, "write failed, %d bytes written of %d bytes", n, strlen(str));
#endif
			return(1);
		}
	}

	return(0);
}

void pidfile_close(void)
{
	if(get_nodaemon()) return;

	if(pidfilefh) {
		close(pidfilefh);
		pidfilefh = 0;
		unlink(get_pidfile());
	}
}

/* EOF */
