Hi Ying, Sorry for the delay.
Here it is (Pls see below): Ciao//Anthony >From 11155e72babf481b526e3aaf1d9427c41d718dd5 Mon Sep 17 00:00:00 2001 From: Akbar Rezvanpour <[email protected]> Date: Thu, 1 Jun 2017 17:58:57 +0200 Subject: [PATCH] Introducing tipc-link-watcher In this commit we add tipc-link-watcher which monitors the tipc link status and reports in foreground mode as shown below. Sample output of the program when invoked by following options: ./tipc-link-watcher -d -p 2 -s 2 1.1.2:data1-1.1.1:data1: link reset:0 rx_packets:0 tx_packets:0 rx_loss:0 tx_loss:0 Working :) 1.1.2:data1-1.1.3:data1: link reset:0 rx_packets:0 tx_packets:0 rx_loss:0 tx_loss:0 Working :) 1.1.2:data1-1.1.4:data1: link reset:0 rx_packets:0 tx_packets:0 rx_loss:0 tx_loss:0 Working :) 1.1.2:data0-1.1.1:data0: link reset:4 rx_packets:0 tx_packets:0 rx_loss:0 tx_loss:0 Working :) 1.1.2:data0-1.1.4:data0: link reset:5 rx_packets:0 tx_packets:0 rx_loss:0 tx_loss:0 Working :) 1.1.2:data0-1.1.3:data0: link reset:5 rx_packets:0 tx_packets:0 rx_loss:0 tx_loss:0 Working :) 1.1.2:data0-1.1.1:data1: link reset:0 rx_packets:0 tx_packets:0 rx_loss:0 tx_loss:0 Working :) 1.1.2:data0-1.1.3:data1: link reset:0 rx_packets:0 tx_packets:0 rx_loss:0 tx_loss:0 Working :) The user is always able to dump the tipc link statistics. Further information is available in the README. Signed-off-by: Akbar Rezvanpour <[email protected]> --- .gitignore | 1 + Makefile.am | 2 +- configure.ac | 1 + tipc-link-watcher/Makefile.am | 4 + tipc-link-watcher/README | 233 ++++ tipc-link-watcher/tipc-link-watcher.c | 2143 +++++++++++++++++++++++++++++++++ 6 files changed, 2383 insertions(+), 1 deletion(-) create mode 100644 tipc-link-watcher/Makefile.am create mode 100644 tipc-link-watcher/README create mode 100644 tipc-link-watcher/tipc-link-watcher.c diff --git a/.gitignore b/.gitignore index f544ddfceff9..0ff143dea8ec 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ tipc-pipe/tipc-pipe tipclog/tipclog multicast_blast/mcast_tipc multicast_blast/group_cast +tipc-link-watcher/tipc-link-watcher diff --git a/Makefile.am b/Makefile.am index 0643d78e03b5..5f0d5e979b15 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ SUBDIRS=tipc-pipe ptts demos man scripts if TIPC_LINK_STATE_SUBSCRITION -SUBDIRS+=tipclog multicast_blast +SUBDIRS+=tipclog multicast_blast tipc-link-watcher endif diff --git a/configure.ac b/configure.ac index 0602878bada7..b466d56cc522 100644 --- a/configure.ac +++ b/configure.ac @@ -40,6 +40,7 @@ AC_CONFIG_FILES([ man/Makefile scripts/Makefile tipclog/Makefile + tipc-link-watcher/Makefile ]) AM_CONDITIONAL(WITH_SCRIPTS, false) AC_ARG_ENABLE(scripts, diff --git a/tipc-link-watcher/Makefile.am b/tipc-link-watcher/Makefile.am new file mode 100644 index 000000000000..f8484d07ff45 --- /dev/null +++ b/tipc-link-watcher/Makefile.am @@ -0,0 +1,4 @@ +sbin_PROGRAMS=tipc-link-watcher +tipc_link_watcher_SOURCES=tipc-link-watcher.c +tipc_link_watcher_CFLAGS=-Werror $(shell $(PKG_CONFIG) libmnl --cflags) +tipc_link_watcher_LDFLAGS=$(shell $(PKG_CONFIG) libmnl --libs) diff --git a/tipc-link-watcher/README b/tipc-link-watcher/README new file mode 100644 index 000000000000..5c8a7b901b80 --- /dev/null +++ b/tipc-link-watcher/README @@ -0,0 +1,233 @@ +#The following commits are pre-requisite for this feature. +1. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=78acb1f9b898e85fa2c1e28e700b54b66b288e8d +2. https://lwn.net/Articles/611994/ +#The former commit is implemented in kernel v3.16 and the later in v3.19. So this tool will work only from v3.19. + +#Install the shared library libmnl-dev on your build environment. + +=== To Build tipc-link-watcher === + +#To build the daemon (SuSE environment): + +#First install libmnl on your build host: (libmnl-1.0.4) + +"zypper install libmnl-devel" + +#and run: + +"gcc -o tipc-link-watcher $(pkg-config --libs --cflags libmnl) *.c" + +=== 2Do === +- Possible bug in current code or TIPC regarding values of rx_packets and tx_packets. + When a link is reset, rx_packets and tx_packets contain rubbish values until the link is reestablished. + +=== Couple of notes === + +-Option "-x" and "-a" should not be used at the same time. + +-One shot (-x): +You will get one single "layer 1" sample when you run with option "-x". +Setting "-a" option will not change this behaviour. + +-Run forever: +When all layer 0 samples (s) are taken every p second, the window +layer 0 start to slide. Afterwards, every p seconds "mean of samples of layer 0" + are calculated. The calculated mean value will be the sample that is inserted into layer 1 window. +The procedure above will continue to fill the layer 1 window until the layer 1 window begin to slide. +When layer 1 window begin to slide the oldest value will be deleted from that window. +The size of layer 1 window is set by option "-a" + +=== So what is "layer 0 and layer 1"? === +Note: + -layer 0 samples are statistic values obtained from TIPC. + -layer 1 samples are "mean of samples of full layer 0 window". + +When Layer 0 is complete, it starts to "slide" and the first sample (mean of layer 0 samples) of layer 1 is +calculated and inserted in layer 1 samples. + +-P 4 : Sample period (how often) in secounds (Default:60 secounds) +-s 5 : Number of samples in the window(layer 0) which must be gt 1 + Default:60 samples +-a 4 : Number of samples in the window layer 1 which must be gt 1 + These samples are mean of layer zero samples over full window + size defined by -s option + Default value is 3600 samples. + __________ _______________ + | 3 | 2 |2 |1 |2 | + |____|____|____|____|____| + <-p-> + <----------- s ----------> + +3+2+2+1+2=10 +The mean value = 10 / 5(-s) = 2 +"2" is mean of layer 0 samples, which is sample number one in layer 1. + + layer 1 + _____________________ + | 2 | | | | + |____|____|____|____| + <-p-> + <------------ a ----> + +Layer 0 received a new sample (8) and the second +sample (mean of layer 0 samples) of layer 1 is calculated and inserted in layer 1 samples.! + + layer 0 + __________ _______________ + | 8 | 3 |2 |2 |1 | + |____|____|____|____|____| + <-p-> + <----------- s ----------> + +8+3+2+2+1=15 +The mean value = 15 / 5(-s) = 3 +"3" is mean of layer 0 samples, which is sample number two in layer 1. + + layer 1 + _____________________ + | 2 | 3 | | | + |____|____|____|____| + <-p-> + <----------- a ----> + +Layer 1 windown mean samples are generated at the same rate as the -p value. +=== Examples how to run the Deamon === + +#3.1: Run forever(absence of -x option); do not run as Daemon(-d); + every second(-p); 60 samples (-s: window size layer 0) and + 3600 mean samples (-a:Default, window size layer 1). + +"tipc-link-watcher -d -p 1 -s 60" + +To make the process generate samples into a file run: +"kill -HUP $(pidof tipc-link-watcher)" + +#3.2: Run forever as a daemon, every second(-p), + 60 samples (-s) and 3600 mean samples (-a:Default). + +"tipc-link-watcher -p 1 -s 60" + +To request samples run: +"kill -HUP $(pidof tipc-link-watcher)" + +#3.3: One shot(-x), do not run as daemon(-d), every 1 second (-p), 60 samples (-s) and dump the samples in "/my/print/path" when the daemon exits. + +"tipc-link-watcher -dx -p 1 -s 60 -f /my/print/path" + +To request additional samples before the daemon exist: +"kill -HUP $(pidof tipc-link-watcher)" + +#3.4: Run forever (last 24 hour) and do not run as daemon(-d), + every 60 seconds (-p:Default), 60 samples (-s:Default). 3600 mean samples (-a:Default). + + "-tipc-link-watcher -d" + + To request samples run: + "kill -HUP $(pidof tipc-link-watcher)" + +Be aware that after 24 hours window layer 1 begins to slide and oldest mean value are removed from the window. + +#3.5:Run forever (latest 24 hour) as a daemon, + every 60 seconds (-p:Default), 60 samples (-s:Default). 3600 mean samples (-a:Default). + + "-tipc-link-watcher" + + To request samples run: + "kill -HUP $(pidof tipc-link-watcher)" + +Be aware that after 24 hours window layer 1 begins to slide and oldest mean value are removed from the window. + + +#3.5:Run for one hour (-x), as daemon, every 60 seconds (-p:Default), 60 samples (-s:Default) + dump the samples in "tmp" directory at exit. + "-tipc-link-watcher -x" + + To request samples run: + "kill -HUP $(pidof tipc-link-watcher)" + +#3.6: Run forever; do not run as Daemon(-d), every second(-p), 2 samlpes (-s) and set thresholds (-o). + +"tipc-link-watcher -d -p 1 -s 2 -o t1_rx_loss_rate=10 -o rx_packets=666" + +The thresholds warnings will be written in syslog. + +#3.7: Run forever, do not run as daemon(-d), every second(-p) and 60 samples (-s), 3600 mean samples (-a:Default), with selected links to watch (-l) + +"tipc-link-watcher -d -p 1 -s 60 -l 1.1.1:bond0-1.1.3:bond0 -l 1.1.1:bond0-1.1.4:bond0" + +The program will print the quality levels of selected links: (looks like this for a link ) +"1.1.1:bond0-1.1.2:bond0: link reset:0 rx_packets:11 tx_packets:71 rx_loss:0 tx_loss:0 Working :)" +Possible quality levels are: Working, Degraded, Faulty and Down + +Watching(-l) links quality is only possible in "non" daemon mode. + +The sample files are NOT effected by "-l" option. That means that you will still get statistic for all published links. + +Note: "rx_packets and tx_packets" are mean values of window layer 0! + "rx_loss and tx_loss" are presented in precent. + "link reset" is number of reset of the link! + + +Attention: There is some bug in TIPC or this code regarding values of + rx_packets and tx_packets. + When any link is reset, rx_packets and tx_packets are rabish values until + the link is restablished. +=== Tipc link quality === + +t1: Threshold one! 10% +t2: Threshold two! 30% + +This is how the link quality is calculated: +Down == (Tipc link is not up) + +Working == (Tipc link is up) && + (Tipc link not reset during a size of window layer 0, which is -s) && + ((rx loss rate < t1) && (tx loss rate < t1)) + +Degraded == (Tipc link is up) && + ((link rested: up->Down->up) || + !((rx loss rate < 10 %) && (tx loss rate < 10 %))) && + ((rx loss rate < t2) && (tx loss rate < t2)) + +Faulty == (Tipc link is up) && + ((link rested: up->Down->up) || + !((rx loss rate < 10 %) && (tx loss rate < 10 %))) && + !((rx loss rate < t2) && (tx loss rate < t2)) + + +=== Couple of notes === + +-Option "-x" and "-a" should not be used at the same time. + +-One shot (-x): + 1-One full set of samples which are represented by values in layer 0 window of samples. The first sample in + the file represents the accumulated staticstic since last time the link statistics were reset by TIPC. + 2-And One single "layer 1" sample which is mean of samples in layer 0 window. + Setting "-a" option will not change this behaviour. + +-Run forever(absence of -x): + When all layer 0 samples (s) are taken every p second, the window + layer 0 start to slide. Afterwards, every p seconds "mean of samples of + layer 0" are calculated. The calculated mean value will be the sample + that is inserted into layer 1 window. The procedure above will continue + to fill the layer 1 window until the layer 1 window begin to slide. + When layer 1 window begin to slide the oldest value will be deleted + from that window. + The size of layer 1 window is set by option "-a" + +-The dumped sample files name look like this: (in csv format) + 2017-05-30.14.26.28_tipc (layer 0 samples) + 2017-05-30.14.26.28_tipc_l1 (layer 1 samples; which are mean values of + layer 0 samples for every "-p" over every "-s" samples) + +-The link "up" and "active" statistics in *_tipc_l1 are the sum + of (not mean of) the link "up" and "active" statistics in window layer 0. + +-"Mean of" some of properties; such as, "dest" and "mtu" in "*_tipc_l1" file; it does + not make sence. + The advantage of mean calculation for these properties is, to reveal any changes of + these values during sampling time. + For example: if "mtu" is configured to the value "1500" the mean value of "mtu" + in window layer 0 should of course be equal to "1500". + +=== That's all folks === diff --git a/tipc-link-watcher/tipc-link-watcher.c b/tipc-link-watcher/tipc-link-watcher.c new file mode 100644 index 000000000000..c7c36fce7397 --- /dev/null +++ b/tipc-link-watcher/tipc-link-watcher.c @@ -0,0 +1,2143 @@ +/* + * tipc-link-watcher.c: TIPC link Fault detection tool + */ + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <stddef.h> +#include <unistd.h> +#include <sys/timerfd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <linux/kernel.h> +#include <signal.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <syslog.h> +#include <fcntl.h> +#include <sys/resource.h> +#include <linux/tipc.h> +#include <linux/tipc_netlink.h> + +#include <pthread.h> + +#include <linux/genetlink.h> +#include <libmnl/libmnl.h> + +#define RUN "/run" +#define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) +static char LOCKFILE[512] = {}; + +#define RESET "\x1b[0m" +#define RED "\x1b[31m" +#define GREEN "\x1b[32m" +#define YELLOW "\x1b[33m" + +#define MAX_MY_TIPC_RESOURCE 0x400 +#define SLIDING_WINDOW_L1_SIZE 3600 + +#define DEFUALT_THRESHOLD_ONE_STATS_RESET_RATE 0 +#define DEFUALT_THRESHOLD_ONE_STATS_RX_LOSS_RATE 10 +#define DEFUALT_THRESHOLD_ONE_STATS_TX_LOSS_RATE 10 +#define DEFUALT_THRESHOLD_TWO_STATS_RESET_RATE 0 +#define DEFUALT_THRESHOLD_TWO_STATS_RX_LOSS_RATE 10 +#define DEFUALT_THRESHOLD_TWO_STATS_TX_LOSS_RATE 10 + +static int first_sample[MAX_MY_TIPC_RESOURCE]; +static int stat_index, l1_stat_index; +static int topsrv_sd, timer_fd; +static unsigned int this_node; +static char start_time[64]; +static bool take_sample = false; + +#define IF_WATCH(W, IN_LIST, FUNC) \ + do { \ + if (W) { if (IN_LIST) FUNC; } else {FUNC; } \ + } \ + while (0) + +#define TIPC_ALARM(EXP, S, T, V, N) \ + do { if (EXP) \ + syslog (LOG_WARNING, " %s"" %s" ":%d" " crossed the threshold value " "%d", N, S, V, T); } \ + while (0) + +#define SET_OFFSET_AND_STEP(OFFSETPOINTER, STEP, OFFSET, STRUCT) \ + do { \ + OFFSETPOINTER = (uintptr_t *)((char *)OFFSET + offsetof(tipceLinkStatistics, STRUCT)); \ + int i = STEP; \ + while (i > 0 ) { \ + OFFSETPOINTER++; \ + i--; \ + } \ + } \ + while (0) + +#define SET_MEM_AND_STEP(OFFSETPOINTER, STEP_SIZE, MEM) \ + do { \ + *OFFSETPOINTER = (uintptr_t)MEM; \ + OFFSETPOINTER++; \ + MEM += STEP_SIZE; \ + } \ + while (0) + +enum { + THRESHOLD_UNSPEC, + THRESHOLD_ONE_STATS_RESET_RATE, + THRESHOLD_ONE_STATS_RX_LOSS_RATE, + THRESHOLD_ONE_STATS_TX_LOSS_RATE, + THRESHOLD_TWO_STATS_RESET_RATE, + THRESHOLD_TWO_STATS_RX_LOSS_RATE, + THRESHOLD_TWO_STATS_TX_LOSS_RATE, + THRESHOLD_MAX +}; + +char * const thresholds[] = { + [THRESHOLD_UNSPEC] = "thresholed_unspec", + [THRESHOLD_ONE_STATS_RESET_RATE] = "t1_reset_rate", + [THRESHOLD_ONE_STATS_RX_LOSS_RATE] = "t1_rx_loss_rate", + [THRESHOLD_ONE_STATS_TX_LOSS_RATE] = "t1_rx_loss_rate", + [THRESHOLD_TWO_STATS_RESET_RATE] = "t2_reset_rate", + [THRESHOLD_TWO_STATS_RX_LOSS_RATE] = "t2_rx_loss_rate", + [THRESHOLD_TWO_STATS_TX_LOSS_RATE] = "t2_tx_loss_rate", + [THRESHOLD_MAX] = NULL, +}; + +enum { + PRIV_TIPC_NLA_STATS_LINK_STATE, + PRIV_TIPC_NLA_STATS_RX_PACKETS, + PRIV_TIPC_NLA_STATS_TX_PACKETS, + PRIV_TIPC_NLA_STATS_RX_LOSS_RATE, + PRIV_TIPC_NLA_STATS_TX_LOSS_RATE, + __PRIV_TIPC_NLA_STATS_MAX +}; + +char * const stat_opts[] = { + [TIPC_NLA_STATS_UNSPEC] = "unspec", + [TIPC_NLA_STATS_RX_INFO] = "rx_info", + [TIPC_NLA_STATS_RX_FRAGMENTS] = "rx_fragments", + [TIPC_NLA_STATS_RX_FRAGMENTED] = "rx_fragmented", + [TIPC_NLA_STATS_RX_BUNDLES] = "rx_bundles", + [TIPC_NLA_STATS_RX_BUNDLED] = "rx_bundled", + [TIPC_NLA_STATS_TX_INFO] = "tx_info", + [TIPC_NLA_STATS_TX_FRAGMENTS] = "tx_fragments", + [TIPC_NLA_STATS_TX_FRAGMENTED] = "tx_fragmented", + [TIPC_NLA_STATS_TX_BUNDLES] = "tx_bundles", + [TIPC_NLA_STATS_TX_BUNDLED] = "tx_bundled", + [TIPC_NLA_STATS_MSG_PROF_TOT] = "msg_prof_tot", + [TIPC_NLA_STATS_MSG_LEN_CNT] = "msg_len_cnt", + [TIPC_NLA_STATS_MSG_LEN_TOT] = "msg_len_tot", + [TIPC_NLA_STATS_MSG_LEN_P0] = "msg_len_0", + [TIPC_NLA_STATS_MSG_LEN_P1] = "msg_len_1", + [TIPC_NLA_STATS_MSG_LEN_P2] = "msg_len_2", + [TIPC_NLA_STATS_MSG_LEN_P3] = "msg_len_3", + [TIPC_NLA_STATS_MSG_LEN_P4] = "msg_len_4", + [TIPC_NLA_STATS_MSG_LEN_P5] = "msg_len_5", + [TIPC_NLA_STATS_MSG_LEN_P6] = "msg_len_6", + [TIPC_NLA_STATS_RX_STATES] = "rx_states", + [TIPC_NLA_STATS_RX_PROBES] = "rx_probes", + [TIPC_NLA_STATS_RX_NACKS] = "rx_nacks", + [TIPC_NLA_STATS_RX_DEFERRED] = "rx_defs", + [TIPC_NLA_STATS_TX_STATES] = "tx_states", + [TIPC_NLA_STATS_TX_PROBES] = "tx_probes", + [TIPC_NLA_STATS_TX_NACKS] = "tx_nacks", + [TIPC_NLA_STATS_TX_ACKS] = "tx_acks", + [TIPC_NLA_STATS_RETRANSMITTED] = "retrans", + [TIPC_NLA_STATS_DUPLICATES] = "dups", + [TIPC_NLA_STATS_LINK_CONGS] = "link_congestion", + [TIPC_NLA_STATS_MAX_QUEUE] = "send_queue_max", + [TIPC_NLA_STATS_AVG_QUEUE] = "send_queue_avg", + [__TIPC_NLA_STATS_MAX] = NULL +}; + +char * const stat_opts_priv[] = { + /* EXTENDED PART*/ + [PRIV_TIPC_NLA_STATS_LINK_STATE] = "link_state", + [PRIV_TIPC_NLA_STATS_RX_PACKETS] = "rx_packets", + [PRIV_TIPC_NLA_STATS_TX_PACKETS] = "tx_packets", + [PRIV_TIPC_NLA_STATS_RX_LOSS_RATE] = "rx_loss", + [PRIV_TIPC_NLA_STATS_TX_LOSS_RATE] = "tx_loss", + [__PRIV_TIPC_NLA_STATS_MAX] = NULL +}; + +char * const stat_opts_link[] = { + [TIPC_NLA_LINK_UNSPEC] = "unspec", + [TIPC_NLA_LINK_NAME] = "link", + [TIPC_NLA_LINK_DEST] = "dest", + [TIPC_NLA_LINK_MTU] = "mtu", + [TIPC_NLA_LINK_BROADCAST] = "broadcast", + [TIPC_NLA_LINK_UP] = "up", + [TIPC_NLA_LINK_ACTIVE] = "active", + [TIPC_NLA_LINK_PROP] = "prop", + [TIPC_NLA_LINK_STATS] = "stats", + [TIPC_NLA_LINK_RX] = "link_rx", + [TIPC_NLA_LINK_TX] = "link_tx", + [__TIPC_NLA_LINK_MAX] = NULL +}; + +uint32_t link_stat_threshold[__TIPC_NLA_STATS_MAX]; +uint32_t link_stat_threshold_priv[__PRIV_TIPC_NLA_STATS_MAX]; +uint32_t link_info_threshold[__TIPC_NLA_LINK_MAX]; + +uint32_t thresholds_link[THRESHOLD_MAX+1] = + {0, + DEFUALT_THRESHOLD_ONE_STATS_RESET_RATE, + DEFUALT_THRESHOLD_ONE_STATS_RX_LOSS_RATE, + DEFUALT_THRESHOLD_ONE_STATS_TX_LOSS_RATE, + DEFUALT_THRESHOLD_TWO_STATS_RESET_RATE, + DEFUALT_THRESHOLD_TWO_STATS_RX_LOSS_RATE, + DEFUALT_THRESHOLD_TWO_STATS_TX_LOSS_RATE + }; + +typedef struct link_miscellaneous { + char *link_state; /* TIPC_NLA_LINK_ACTIVE, TIPC_NLA_LINK_UP */ + uint32_t *rx_packets; /* TIPC_NLA_LINK_RX - TIPC_NLA_STATS_RX_INFO */ + uint32_t *tx_packets; /* TIPC_NLA_LINK_TX - TIPC_NLA_STATS_TX_INFO */ + uint32_t *rx_loss; /* perc(TIPC_NLA_STATS_RX_NACKS, + PRIV_TIPC_NLA_STATS_TX_PACKETS) */ + uint32_t *tx_loss; /* prec(TIPC_NLA_STATS_TX_NACKS, + PRIV_TIPC_NLA_STATS_RX_PACKETS) */ +}link_miscellaneous; + +/* Link info */ +typedef struct link_information { + uint32_t *unspec; + char *link; /* TIPC_NLA_LINK_NAME */ + uint32_t *dest; /* TIPC_NLA_LINK_DEST */ + uint32_t *mtu; /* TIPC_NLA_LINK_MTU */ + uint32_t *broadcast; /* TIPC_NLA_LINK_BROADCAST */ + uint32_t *up; /* TIPC_NLA_LINK_UP */ + uint32_t *active; /* TIPC_NLA_LINK_ACTIVE */ + uint32_t *prop; /* TIPC_NLA_LINK_PROP */ + uint32_t *stats; /* TIPC_NLA_LINK_STATS */ + uint32_t *link_rx; /* TIPC_NLA_LINK_RX */ + uint32_t *link_tx; /* TIPC_NLA_LINK_TX */ +}link_information; + +/* Nest, link propreties. Valid for link, media and bearer */ +typedef struct nest_link_propreties { + uint32_t *unspec; + uint32_t *priority; /* TIPC_NLA_PROP_PRIO */ + uint32_t *tolerance; /* TIPC_NLA_PROP_TOL */ + uint32_t *window; /* TIPC_NLA_PROP_WIN */ +}link_propreties; + +/* Nest, statistics info */ +typedef struct nest_stat_information { + uint32_t *unspec; + uint32_t *rx_info; /* TIPC_NLA_STATS_RX_INFO */ + uint32_t *rx_fragments; /* TIPC_NLA_STATS_RX_FRAGMENTS */ + uint32_t *rx_fragmented; /* TIPC_NLA_STATS_RX_FRAGMENTED */ + uint32_t *rx_bundles; /* TIPC_NLA_STATS_RX_BUNDLED */ + uint32_t *rx_bundled; /* TIPC_NLA_STATS_RX_BUNDLED */ + uint32_t *tx_info; /* TIPC_NLA_STATS_TX_INFO */ + uint32_t *tx_fragments; /* TIPC_NLA_STATS_TX_FRAGMENTS */ + uint32_t *tx_fragmented; /* TIPC_NLA_STATS_TX_FRAGMENTED */ + uint32_t *tx_bundles; /* TIPC_NLA_STATS_TX_BUNDLES */ + uint32_t *tx_bundled; /* TIPC_NLA_STATS_TX_BUNDLED */ + uint32_t *msg_prof_tot; /* TIPC_NLA_STATS_MSG_PROF_TOT */ + uint32_t *len_cnt; /* TIPC_NLA_STATS_MSG_LEN_CNT */ + uint32_t *len_tot; /* TIPC_NLA_STATS_MSG_LEN_TOT */ + uint32_t *len_0; /* TIPC_NLA_STATS_MSG_LEN_P0 */ + uint32_t *len_1; /* TIPC_NLA_STATS_MSG_LEN_P1 */ + uint32_t *len_2; /* TIPC_NLA_STATS_MSG_LEN_P2 */ + uint32_t *len_3; /* TIPC_NLA_STATS_MSG_LEN_P3 */ + uint32_t *len_4; /* TIPC_NLA_STATS_MSG_LEN_P4 */ + uint32_t *len_5; /* TIPC_NLA_STATS_MSG_LEN_P5 */ + uint32_t *len_6; /* TIPC_NLA_STATS_MSG_LEN_P6 */ + uint32_t *rx_states; /* TIPC_NLA_STATS_RX_STATES */ + uint32_t *rx_probes; /* TIPC_NLA_STATS_RX_PROBES */ + uint32_t *rx_nacks; /* TIPC_NLA_STATS_RX_NACKS */ + uint32_t *rx_defs; /* TIPC_NLA_STATS_RX_DEFERRED */ + uint32_t *tx_states; /* TIPC_NLA_STATS_TX_STATES */ + uint32_t *tx_probes; /* TIPC_NLA_STATS_TX_PROBES */ + uint32_t *tx_nacks; /* TIPC_NLA_STATS_TX_NACKS */ + uint32_t *tx_acks ; /* TIPC_NLA_STATS_TX_ACKS */ + uint32_t *retrans; /* TIPC_NLA_STATS_RETRANSMITTED */ + uint32_t *dups; /* TIPC_NLA_STATS_DUPLICATES */ + uint32_t *link_congestion;/* TIPC_NLA_STATS_LINK_CONGS */ + uint32_t *send_queue_max; /* TIPC_NLA_STATS_MAX_QUEUE */ + uint32_t *send_queue_avg; /* TIPC_NLA_STATS_AVG_QUEUE */ +}stat_information; + +typedef struct link_status { + uint64_t up; + uint64_t down; + uint64_t count_down; +}link_status; + +typedef struct linkStatistics { + /* link misc */ + link_miscellaneous link_misc; + + /* Link info */ + link_information link_info; + + /* Nest, link propreties. Valid for link, media and bearer */ + link_propreties link_props; + + /* Nest, statistics info */ + stat_information stat_info; + + /* Current link status */ + link_status link_state; +}tipceLinkStatistics; + + +/* FIXME: This is a copy from struct from socket.c libmnl*/ +struct mnl_socket { + int fd; + struct sockaddr_nl addr; +}; + +struct tipc_link_name_map { + struct tipc_sioc_ln_req req; + SLIST_ENTRY(tipc_link_name_map) entries; +}; + +SLIST_HEAD(listhead, tipc_link_name_map) head = SLIST_HEAD_INITIALIZER(head); + +typedef struct event_status { + char *resource; + uint32_t event; +}event_status; + +event_status tipc_events[2] = + {{.resource = "l_event", .event = TIPC_LINK_STATE}, + {.resource = "n_event", .event = TIPC_CFG_SRV}}; + +typedef struct resource_status { + char *name; + uint64_t up; + uint64_t down; +}resource_status; + +resource_status *node_status = NULL; + +typedef struct data { + int links; + int sample_limit; + char *mem; + tipceLinkStatistics *samples; + int counter[1000]; +}data; + +data uc_d = { + .links = 0, + .sample_limit = 0, + .mem = NULL, + .samples = NULL +}; +data *uc_dp = &uc_d; + +data l1_d = { + .links = 0, + .sample_limit = 0, + .mem = NULL, + .samples = NULL +}; +data *l1_dp = &l1_d; + +#define uc_links uc_dp->links +#define uc_samples uc_dp->samples +#define uc_tot_mem uc_dp->mem + +typedef struct action_resource { + int action; + char *action_list[MAX_MY_TIPC_RESOURCE]; +}action_resource; + +action_resource w_link, w_node, r_link; + +action_resource *w_linkp = &w_link; +action_resource *w_nodep = &w_node; +action_resource *r_linkp = &r_link; + +char *user_dir = NULL; +typedef struct l0_default_conf { + int period; + int nos; + bool window_passed; + int nod; + int one_shot; +}l0_default_conf; + +/* default config */ +/* Run as Daemon every minute in one hour and quit without dumping any file*/ +l0_default_conf l0_w_conf = { + .period = 60, /* Number of secounds between samples*/ + .nos = 60, /* Number of samples*/ + .window_passed = false, + .nod = 0, /* Daemonize, 1 => Not Daemonize*/ + .one_shot = 0, /* Just run one layer zero window */ +}; + +typedef struct l1_default_conf { + int counter; /* Counter to discover when the window start to slid*/ + int nos; /* The size of the sliding window*/ + bool window_passed; + +}l1_default_conf; + +/* default l1 config */ +l1_default_conf l1_w_conf = { + .counter = 0, + .nos = SLIDING_WINDOW_L1_SIZE, /* Maximum Number of collected means */ + .window_passed = false +}; + +typedef void (*add_link) (const char *, + const char *, + int, + data **); +typedef void(*print_sample_to_file) (char *, int, char *, + char *, int, int, + tipceLinkStatistics **); + +static void usage(char *pName) +{ + int i; + fprintf(stdout, + "usage: %s [options]\n" + "options:\n" + " -h Print this message\n" + " -a [value] Number of samples in the window layer 1 which must be gt 0 \n" + " These samples are mean of layer 0 full window samples (Default: 3600 samples)\n" + " -d Do not daemonize; i.e. non daemon mode\n" + " -f [value] The directory to save the sample files in csv format with following postfixes:\n" + " \"_tipc\" and \"_tipc_l1\" (Default: \"%s\" directory)\n" + " -l [value] Link(s) to watch, only in non daemon mode\n" + " -n [value] Node(s) to watch, only in non daemon mode\n" + " -p [value] Sample period (how often) in secounds (Default: 60 secounds)\n" + " -o [option=value] Threshold options: Threshold option value must be gt 0\n" + " -s [value] Number of samples in the window layer 0 which must be gt 1 (Default: 60 samples)\n" + " -x Run only one window of samples and exit the program; i.e. \"-s\" samples that are \n" + " written to \"_tipc\" file; and one single \"mean of layer 0 window\" sample;\n" + " which is written to \"_tipc_l1\" file\n" + "\n" + " Only tipc \"unicast\" messages are sampled!\n" + " Threshold options are only valid for following statistics:\n", + pName, P_tmpdir); + for (i = THRESHOLD_ONE_STATS_RESET_RATE; i < THRESHOLD_MAX; i ++) + fprintf(stdout, " %s \n", thresholds[i]); + + for (i = TIPC_NLA_STATS_RX_INFO; i < (TIPC_NLA_STATS_MAX + 1); i ++) + fprintf(stdout, " %s \n", stat_opts[i]); + + for (i = PRIV_TIPC_NLA_STATS_RX_PACKETS; i < __PRIV_TIPC_NLA_STATS_MAX; i ++) + fprintf(stdout, " %s \n", stat_opts_priv[i]); + fprintf(stdout, " link_rx\n link_tx \n\n"); + fprintf(stdout,"-o option example: %s -d -o t1_rx_loss_rate=10 -o rx_packets=666 -o tx_packets=123117\n", pName); + fprintf(stdout,"**************************\n"); +} + +static void sigs_term_int(int signo) +{ + syslog(LOG_INFO, "The TIPC Network Daemon has terminated by %s signal", + strsignal(signo)); + exit(0); +} + +static void sig_hup(int signo) +{ + syslog(LOG_INFO, "Create the csv sample file(s)"); + take_sample = true; +} + +static void handle_signals (void) +{ + struct sigaction sa; + + sa.sa_handler = sigs_term_int; + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGHUP); + sa.sa_flags = 0; + + if (sigaction(SIGINT, &sa, NULL) < 0) + { + syslog(LOG_ERR, "sigaction: Failed to catch SIGINT, %s", strerror(errno)); + exit(-1); + } + if (sigaction(SIGTERM, &sa, NULL) < 0) + { + syslog(LOG_ERR, "sigaction: Failed to catch SIGTERM, %s", + strerror(errno)); + exit(-1); + } + + sa.sa_handler = sig_hup; + sigemptyset(&sa.sa_mask); + + sigaddset(&sa.sa_mask, SIGINT); + sa.sa_flags = SA_RESTART; + if (sigaction(SIGHUP, &sa, NULL) < 0) + { + syslog(LOG_ERR, "sigaction: Failed to catch SIGHUP, %s", strerror(errno)); + exit(-1); + } +} + +static uint32_t calc_sum(int samples, uint32_t *array) +{ + int sample; + uint32_t sum = 0; + + for (sample = 0; sample < samples; sample++) + sum = sum + array[sample]; + + return sum; +} + +static uint32_t calc_mean(int index, int samples, uint32_t **array, int first) +{ + int sample, skip = 0; + uint32_t sum = 0, *data = *array; + + if (samples == 0 ) + return 0; + + for (sample = 0; sample < samples; sample++) { + index = (index + 1) % samples; + if (first == 0 && index == 0) + { + skip = 1; + } else { + sum = sum + data[index]; + } + } + if (skip) + { + if ((samples - 1) == 0 ) + return 0; + return (sum/(samples - 1)); + } else { + return(sum/samples); + } +} + +static uint32_t perc(uint32_t count, uint32_t total) +{ + if(total > 0) + return (count * 100 + (total / 2)) / total; + return 0; +} + +static void print_samples(FILE *csv_fp, + const char *opt, + uint32_t *stat, + int nos, + int s_index) +{ + int sample=0; + char *buf = malloc(strlen(opt) + 2); + if (!buf) + { + syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s", + strerror(errno)); + exit(-1); + } + sprintf(buf, "%s,", opt); + fprintf(csv_fp,"%s %u", buf, stat[0]); + for (sample = 1 ; sample < nos; sample++) { + s_index = (s_index + 1 ) % nos; + fprintf(csv_fp,"%s %u", ",", stat[s_index]); + } + fprintf(csv_fp,"\n"); + free(buf); +}; + +static void print_uc_samples_to_file(char *filename, + int link, + char *start_time, char *end_time, + int s_index, + int nos, + tipceLinkStatistics **samples) +{ + FILE *csv_fp; + uintptr_t *offset_p; + int i; + uint32_t *stat_p; + + csv_fp=fopen(filename,"a+"); + fprintf(csv_fp,"stime: %s , etime: %s,", start_time, end_time); + fprintf(csv_fp,"\n"); + fprintf(csv_fp,"%s, ", (*samples)[link].link_info.link); + fprintf(csv_fp,"mtu(%u), ", (*samples)[link].link_info.mtu[link]); + fprintf(csv_fp,"priority(%u), ", (*samples)[link].link_props.priority[link]); + fprintf(csv_fp,"tolerance(%u ms), ", (*samples)[link].link_props.tolerance[link]); + fprintf(csv_fp,"window(%u packets), ", (*samples)[link].link_props.window[link]); + fprintf(csv_fp,"link_up(%lu), link_down(%lu)", + (*samples)[link].link_state.up, (*samples)[link].link_state.down); + fprintf(csv_fp,"\n"); + /* skip link_state*/ + SET_OFFSET_AND_STEP(offset_p, 1, &(*samples)[link], link_misc); + for (i = PRIV_TIPC_NLA_STATS_RX_PACKETS; i < __PRIV_TIPC_NLA_STATS_MAX; i++) { + stat_p = (uint32_t *)((uintptr_t)*offset_p); + print_samples(csv_fp, stat_opts_priv[i], stat_p, nos, s_index); + offset_p++; + } + SET_OFFSET_AND_STEP(offset_p, 2, &(*samples)[link], link_info); + for (i = TIPC_NLA_LINK_DEST; i < __TIPC_NLA_LINK_MAX; i++) { + stat_p = (uint32_t *)((uintptr_t)*offset_p); + if( i == TIPC_NLA_LINK_UP || i == TIPC_NLA_LINK_ACTIVE || + i == TIPC_NLA_LINK_RX || i == TIPC_NLA_LINK_TX) + print_samples(csv_fp, stat_opts_link[i], stat_p, nos, s_index); + offset_p++; + } + SET_OFFSET_AND_STEP(offset_p, 1, &(*samples)[link], stat_info); + for (i = TIPC_NLA_STATS_RX_INFO; i < __TIPC_NLA_STATS_MAX; i++) { + stat_p = (uint32_t *)((uintptr_t)*offset_p); + print_samples(csv_fp, stat_opts[i], stat_p, nos, s_index); + offset_p++; + } + fclose(csv_fp); +}; + +static char * get_full_path_file_name(const char * post_str) +{ + time_t the_time = time(NULL); + /* If threadsafe is wished, use localtime_r*/ + struct tm *tm = localtime(&the_time); + char tmp[300], *fullpath, *csv_file; + + strftime(tmp, sizeof(tmp),"%Y-%m-%d.%H.%M.%S",tm); + fullpath = malloc(300*strlen(tmp) + 2); + if (!fullpath) { + syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s", + strerror(errno)); + exit(-1); + } + sprintf(fullpath, "%s/%s", user_dir ? user_dir : P_tmpdir, tmp); + strcat(fullpath, post_str); + csv_file = malloc(150*strlen(tmp) + 2); + if (!csv_file) + { + syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s", + strerror(errno)); + exit(-1); + } + strcpy (csv_file, fullpath); + free(fullpath); + return csv_file; +} + +static void print_to_file(char* csv_file_post_str, + int s_index, + int nos, + print_sample_to_file print2file, + data **dp) +{ + int j; + char end_time[64]; + time_t the_time = time(NULL); + /* If threadsafe is wished, use localtime_r*/ + struct tm *tm = localtime(&the_time); + strftime(end_time, sizeof(end_time),"%Y-%B-%d:%H:%M:%S",tm); + char *csv_file = get_full_path_file_name(csv_file_post_str); + + for (j = 0; j < (*dp)->links; j++) { + (*print2file)(csv_file, j, start_time, end_time, s_index, nos, &((*dp)->samples)); + } + free(csv_file); +} + +static void handle_print_to_file(void) +{ + print_to_file("_tipc", + stat_index, + l0_w_conf.window_passed ? l0_w_conf.nos : stat_index, + print_uc_samples_to_file, + &uc_dp); + + if(l0_w_conf.window_passed) + print_to_file("_tipc_l1", + l1_stat_index, + l1_w_conf.window_passed ? l1_w_conf.nos : l1_stat_index, + print_uc_samples_to_file, + &l1_dp); +} + +static bool check_link_reset(int link, + data *dp) +{ + return ((dp->samples)[link].link_state.count_down == 0 ); +} + +static bool check_link_up(int link, data *dp, int s_index) +{ + return ((dp->samples)[link].link_info.up[s_index] == 0 ); +} + +static bool check_threshold_one(int link, data *dp, int s_index) +{ + return (((dp->samples)[link].link_misc.rx_loss[s_index] < + thresholds_link[THRESHOLD_ONE_STATS_RX_LOSS_RATE]) && + ((dp->samples)[link].link_misc.tx_loss[s_index] < + thresholds_link[THRESHOLD_ONE_STATS_TX_LOSS_RATE])); +} + +static bool check_threshold_two_lt(int link, data *dp, int s_index) +{ + return (((dp->samples)[link].link_misc.rx_loss[s_index] < + thresholds_link[THRESHOLD_TWO_STATS_RX_LOSS_RATE]) && + ((dp->samples)[link].link_misc.tx_loss[s_index] < + thresholds_link[THRESHOLD_TWO_STATS_TX_LOSS_RATE])); +} + +static bool check_threshold_two_gt(int link, data *dp, int s_index) +{ + return (((dp->samples)[link].link_misc.rx_loss[s_index] > + thresholds_link[THRESHOLD_TWO_STATS_RX_LOSS_RATE]) || + ((dp->samples)[link].link_misc.tx_loss[s_index] > + thresholds_link[THRESHOLD_TWO_STATS_TX_LOSS_RATE])); +} + +static void link_quality(int link, data *l1_dp, data *uc_dp) +{ + if (!check_link_up(link, uc_dp, stat_index)) + { + printf("Down!\n"); + } else if (check_link_up(link, uc_dp, stat_index) && + check_link_reset(link, uc_dp) && + check_threshold_one(link, l1_dp, l1_stat_index)) + { + printf(GREEN "Working :)" RESET "\n"); + } else if (check_link_up(link, uc_dp, stat_index) && + (!check_link_reset(link, uc_dp) || + !check_threshold_one(link, l1_dp, l1_stat_index)) && + check_threshold_two_lt(link, l1_dp, l1_stat_index)) + { + printf(YELLOW "Degraded :(" RESET "\n"); + } else if (check_link_up(link, uc_dp, stat_index) && + (!check_link_reset(link, uc_dp) || + !check_threshold_one(link, l1_dp, l1_stat_index)) && + !check_threshold_two_gt(link, l1_dp, l1_stat_index)) + { + printf(RED "Faulty!!!" RESET "\n"); + } else { + printf(RED "Faulty .... going down!!!" RESET "\n"); + } +} + +static int already_pid_running(char *cmd) +{ + int fd; + char buf[16]; + struct flock fl; + memset(&fl, 0, sizeof fl); + + snprintf(LOCKFILE, sizeof(LOCKFILE), "%s/%s.pid", RUN, cmd); + fd = open(LOCKFILE, O_RDWR|O_CREAT, LOCKMODE); + if (fd < 0) + { + syslog(LOG_ERR, "open: Failed to open %s -> %s", + LOCKFILE, strerror(errno)); + exit(-1); + } + + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + + if (fcntl(fd, F_SETLK, &fl) < 0) + { + if (errno == EACCES || errno == EAGAIN) { + close(fd); + return(1); + } + syslog(LOG_ERR, "fcntl: Failed to lock %s -> %s", + LOCKFILE, strerror(errno)); + exit(-1); + } + + + if (ftruncate(fd, 0) < 0 ) + { + syslog(LOG_ERR, "ftruncate: Failed to truncate %d, %s", fd, strerror(errno)); + exit(-1); + } + + sprintf(buf, "%ld", (long)getpid()); + + if (write(fd, buf, strlen(buf)+1) < 0 ) + { + syslog(LOG_ERR, "write: Failed to write to %d, %s", + fd, strerror(errno)); + exit(-1); + } + return(0); +} + +static void daemonise(const char *cmd) +{ + int i, fd0, fd1, fd2; + pid_t pid; + struct rlimit rl; + + /* + * Clear file creation mask. + */ + umask(0); + + /* + * Get maximum number of file descriptors. + */ + if (getrlimit(RLIMIT_NOFILE, &rl) < 0) + { + syslog(LOG_ERR, "getrlimit: Failed to get file limit: %s", + strerror(errno)); + exit(-1); + } + + /* + * Become a session leader to lose controlling TTY. + */ + if ((pid = fork()) < 0) + { + syslog(LOG_ERR, "fork: Failed to fork: %s", strerror(errno)); + exit(-1); + } + else if (pid != 0) /* parent */ + exit(0); + setsid(); + + /* + * Ensure future opens won't allocate controlling TTYs. + */ + + /* Second fork */ + if ((pid = fork()) < 0) + { + syslog(LOG_ERR, "fork: Failed to fork: %s", strerror(errno)); + exit(-1); + } + else if (pid != 0) /* parent */ + exit(0); + + /* + * Second child: + * 1-The second child is a child of init now. + * 2-That the child will never acquire a controlling TTY. + */ + + /* + * Change the current working directory to the root so + * we won't prevent file systems from being unmounted. + */ + if (chdir("/") < 0) + { + syslog(LOG_ERR, "chdir: Failed to change directory to /: %s", + strerror(errno)); + exit(-1); + } + /* + * Close all open file descriptors. + */ + if (rl.rlim_max == RLIM_INFINITY) + rl.rlim_max = 1024; + for (i = 0; i < rl.rlim_max; i++) + close(i); + + /* + * Attach file descriptors 0, 1, and 2 to /dev/null. + */ + fd0 = open("/dev/null", O_RDWR); + fd1 = dup(0); + fd2 = dup(0); + + /* + * Initialize the log file. + */ + openlog(cmd, LOG_CONS, LOG_DAEMON); + if (fd0 != 0 || fd1 != 1 || fd2 != 2) + { + syslog(LOG_ERR, "openlog: unexpected file descriptors %d %d %d: %s", + fd0, fd1, fd2, strerror(errno)); + exit(-1); + } +} + +static void daemonize_me(char *cmd) +{ + + if(!l0_w_conf.nod) + { + daemonise(cmd); + if (already_pid_running(cmd)) + { + syslog(LOG_ERR, "daemonize_me: daemon already running"); + exit(-1); + } + } +}; + +static void add_resource(const char *name, + resource_status **resource, + int *limit, + int *count) +{ + (*limit)++; + resource_status new_resource; + memset(&new_resource, 0, sizeof(new_resource)); + resource_status *resource_p = &new_resource; + + resource_status *newArray = realloc(*resource, + (*limit)*sizeof(resource_status)); + if(!newArray) + { + free(*resource); + syslog(LOG_ERR, "realloc: Failed to realloc buffer , %s", + strerror(errno)); + exit(-1); + } else { + resource_p->name = (char*)malloc(strlen(name) + 1); + if (!resource_p->name) + { + syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s", + strerror(errno)); + exit(-1); + } + strncpy(resource_p->name, name, strlen(name)); + *(resource) = newArray; + (*(resource))[(*count)++] = *resource_p; + } + return; +} + +static int look_up_node_index(char * name, + resource_status *node, + int len) +{ + int i; + for (i = 0; i < len ; i++) { + if (!strncmp(name, (node)[i].name, strlen(name))) + return i; + } + return -1; +} + +static int look_up_link_index(char *name, char **links, int len) +{ + int link; + for (link = 0; link < len; link++) { + if (!strncmp(name, links[link], strlen(name))) + return link; + } + return -1; +} + +static int insert_link(const char *name, + const char *link_state, + add_link link2add, + int nos, + data *dp) +{ + int link; + for (link = 0; link < dp->links ; link++) { + if (!strncmp(name, dp->samples[link].link_info.link, strlen(name))) + return link; + } + (*link2add)(name, link_state, nos, &dp); + return (dp->links - 1); +} + +static void uc_add_link(const char *name, + const char * link_state, + int nos, + data **uc_dp) +{ + /* "nos + 1" in the function allocates memory for last sampled value! + * 2bused in the delta calculation! + */ + ((*uc_dp)->sample_limit)++; + int i; + uint16_t size_tot_chars, size_tot_chars_2b_alligend; + uint16_t size_tot_u32s; + + uint16_t step_size = (nos + 1)*sizeof(uint32_t); + tipceLinkStatistics new_l; + tipceLinkStatistics *new_link = &new_l; + tipceLinkStatistics *new_samples = realloc((*uc_dp)->samples, ((*uc_dp)->sample_limit)*sizeof(tipceLinkStatistics)); + char *tot_mem; + + if(!new_samples) + { + free((*uc_dp)->samples); + syslog(LOG_ERR, "uc realloc: Failed to realloc buffer, %s", + strerror(errno)); + exit(-1); + } else { + uintptr_t *offset_p; + + /* Memory to 2b allocted for nos*linkstate and link name */ + size_tot_chars_2b_alligend = strlen(link_state)*(nos + 1 ) + (nos + 1 ) + strlen(name) + 1 ; /* null characters*/ + size_tot_chars = (size_tot_chars_2b_alligend + (4 - 1)) & - 4; /* Round up to 4-byte boundary */ + size_tot_u32s = (sizeof(tipceLinkStatistics) / sizeof(uintptr_t)) - (2); /* const strings*/ + (*uc_dp)->mem = calloc((nos + 1), sizeof(uint32_t)*size_tot_u32s + size_tot_chars); + tot_mem = (*uc_dp)->mem; + /* linkstate miscellaneous */ + new_link->link_misc.link_state = tot_mem; + strncpy(new_link->link_misc.link_state, link_state, strlen(link_state)); + *(new_link->link_misc.link_state + strlen(link_state)) = '\0'; + + /* Make space for nos of linkstate string and null character!*/ + tot_mem = tot_mem + (((strlen(link_state)*nos + nos) + (4 - 1)) & - 4); + SET_OFFSET_AND_STEP(offset_p, 1, new_link, link_misc); + + for ( i = PRIV_TIPC_NLA_STATS_RX_PACKETS; i < __PRIV_TIPC_NLA_STATS_MAX; i++ ) + SET_MEM_AND_STEP(offset_p, step_size, tot_mem); + + new_link->link_info.link = tot_mem; + strncpy(new_link->link_info.link, name, strlen(name)); + *(new_link->link_info.link + strlen(name)) = '\0'; + tot_mem = tot_mem + (((strlen(name) + 1) + (4 - 1)) & - 4); + SET_OFFSET_AND_STEP(offset_p, 0, new_link, link_info); + /* skip unspec*/ + SET_MEM_AND_STEP(offset_p, step_size, tot_mem); + /* skip link pointer */ + offset_p++; + for ( i = TIPC_NLA_LINK_DEST; i < __TIPC_NLA_LINK_MAX; i++ ) + SET_MEM_AND_STEP(offset_p, step_size, tot_mem); + + /* nest link propreties */ + SET_OFFSET_AND_STEP(offset_p, 0, new_link, link_props); + /* skip unspec */ + SET_MEM_AND_STEP(offset_p, step_size, tot_mem); + for ( i = TIPC_NLA_PROP_PRIO; i < __TIPC_NLA_PROP_MAX; i++ ) + SET_MEM_AND_STEP(offset_p, step_size, tot_mem); + + /* nest stat information */ + SET_OFFSET_AND_STEP(offset_p, 0, new_link, stat_info); + SET_MEM_AND_STEP(offset_p, step_size, tot_mem); + for ( i = TIPC_NLA_STATS_RX_INFO; i < __TIPC_NLA_STATS_MAX; i++ ) + SET_MEM_AND_STEP(offset_p, step_size, tot_mem); + + /* link status FIXME: dubble check" if tot_mem includes this part too*/ + SET_OFFSET_AND_STEP(offset_p ,0 ,new_link , link_state); + *offset_p = (uintptr_t)tot_mem; + memset((link_status *)offset_p, 0, sizeof(link_status)); + + (*uc_dp)->samples = new_samples; + ((*uc_dp)->samples)[((*uc_dp)->links)++] = *new_link; + } + return; +} + +static void log_event(int topsrv_sd, + struct tipc_event *evt) +{ + + static int count = 0; + static int limit = 0; + int node; + + char r_name[TIPC_MAX_LINK_NAME]; + char full_name[TIPC_MAX_LINK_NAME + 100]; + struct tipc_link_name_map *link; + struct tipc_sioc_ln_req lnr = { + .peer = ntohl(evt->found_lower), + .bearer_id = ntohl(evt->port.ref) + }; + + if (ntohl(evt->event) == TIPC_PUBLISHED) + { + /* UP*/ + if ((ntohl((*evt).s.seq.type)) == TIPC_LINK_STATE) + { + if (ioctl(topsrv_sd, SIOCGETLINKNAME, &lnr) < 0) + { + syslog(LOG_ERR, "ioctl: Failed, %s", strerror(errno)); + exit(-1); + } else { + strcpy(r_name, lnr.linkname); + sprintf(full_name, "<%s> on network plane %c", + r_name, lnr.bearer_id + 'A'); + } + link = malloc(sizeof(struct tipc_link_name_map)); + if (!link) + { + syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s", + strerror(errno)); + exit(-1); + } + link->req.peer = lnr.peer; + link->req.bearer_id = lnr.bearer_id; + strcpy(link->req.linkname, lnr.linkname); + + SLIST_INSERT_HEAD(&head, link, entries); + + int uc_index = insert_link(r_name, "UP", uc_add_link, + l0_w_conf.nos, uc_dp); + (uc_dp->samples)[uc_index].link_state.up++; + IF_WATCH(w_linkp->action, + ((look_up_link_index(r_name, w_linkp->action_list, w_linkp->action)) >= 0), + syslog(LOG_INFO, "Published link: %s\n", full_name)); + + if (l0_w_conf.window_passed) + if((uc_dp->samples)[uc_index].link_state.down) + IF_WATCH(w_linkp->action, + ((look_up_link_index((uc_dp->samples)[uc_index].link_info.link, + w_linkp->action_list, w_linkp->action)) >= 0), + link_quality(uc_index, l1_dp, uc_dp)); + } else { /* TIPC_CFG_SRV */ + node = ntohl(evt->port.node); + sprintf(r_name, "%u.%u.%u", + tipc_zone(node), tipc_cluster(node), tipc_node(node)); + IF_WATCH(w_nodep->action, + ((look_up_link_index(r_name, w_nodep->action_list, w_nodep->action)) >= 0), + syslog(LOG_INFO, "Published node: %s\n", r_name)); + + node = look_up_node_index(r_name, node_status, count); + if(node < 0) + { + add_resource(r_name, + &node_status, + &limit, + &count); + node = count - 1; + } + (node_status)[node].up++; + } + + } else if (evt->event == htonl(TIPC_WITHDRAWN)) + { + int found = 0; + SLIST_FOREACH(link, &head, entries) { + if ((link->req.peer == lnr.peer) && + (link->req.bearer_id == lnr.bearer_id)) { + strcpy(r_name, link->req.linkname); + SLIST_REMOVE(&head, link, tipc_link_name_map, entries); + free(link); + found = 1; + break; + } + } + if (!found) + { + syslog(LOG_ERR, "Unknown link with peer:%x bearer_id:%x withdrawn!", + lnr.peer, lnr.bearer_id); + return; + } + /* Down*/ + if ((ntohl((*evt).s.seq.type)) == TIPC_LINK_STATE) + { + int link = -1; + for ( link = 0; link < uc_dp->links ; link++ ) { + if (!strncmp(r_name, uc_dp->samples[link].link_info.link, strlen(r_name))) + break; + } + + if (link < 0 ) + { + syslog(LOG_ERR, "Did I? missed to insert the link!"); + exit(-1); + } + (uc_dp->samples)[link].link_state.down++; + (uc_dp->samples)[link].link_state.count_down = l0_w_conf.nos; + + if (l0_w_conf.window_passed) + IF_WATCH(w_linkp->action, + ((look_up_link_index(((uc_dp)->samples)[link].link_info.link, + w_linkp->action_list, w_linkp->action)) >= 0), + link_quality(link, l1_dp, uc_dp)); + IF_WATCH(w_linkp->action, + ((look_up_link_index(r_name, w_linkp->action_list, w_linkp->action)) >= 0), + syslog(LOG_INFO, "Withdrawn link: %s\n", full_name)); + } else { + if(look_up_node_index(r_name, node_status, count) < 0) + { + syslog(LOG_ERR, "Did I? missed to insert the node!"); + exit(-1); + } + printf("Withdrawn node: %s\n", r_name); + } + } + else if (evt->event == htonl(TIPC_SUBSCR_TIMEOUT)) + syslog(LOG_WARNING, "TIPC_SUBSCR_TIMEOUT event"); + else + syslog(LOG_WARNING, "unknown event type (%u)\n", ntohl(evt->event)); + return; +} + +static int unicast_sample(struct nlattr *attrs[], + struct nlattr *prop[], + struct nlattr *stats[], + int index, + data *dp, + const char *name) +{ + int i; + uint32_t *last_p; + uint32_t *stat_p; + uintptr_t *offset_p; + + uint32_t threshold = 0; + stat_information *offset_stat_info_p; + link_information *offset_link_info_p; + link_miscellaneous *offset_link_misc_p; + /*skip unspec and link*/ + SET_OFFSET_AND_STEP(offset_p, 2, &(dp->samples)[index], link_info); + for ( i = TIPC_NLA_LINK_DEST; i < __TIPC_NLA_LINK_MAX; i++) { + stat_p = (uint32_t *)(uintptr_t)*offset_p; + last_p = &stat_p[l0_w_conf.nos]; + stat_p += stat_index; + if ((i < TIPC_NLA_LINK_BROADCAST) || (i > TIPC_NLA_LINK_STATS)) + { + if( i > TIPC_NLA_LINK_STATS) + { + if(dp->counter[index] < (l0_w_conf.nos) ) + { + /* link_rx, link_tx */ + if(stat_index) + { + *stat_p = mnl_attr_get_u32(attrs[i]) - *last_p; + } else { + *stat_p = mnl_attr_get_u32(attrs[i]); + } + } else { + *stat_p = mnl_attr_get_u32(attrs[i]) - *last_p; + } + *last_p = mnl_attr_get_u32(attrs[i]); + threshold = link_info_threshold[i]; + TIPC_ALARM(((threshold > 0) && (*stat_p > threshold)), stat_opts_link[i], threshold, *stat_p, name); + } else { + /*dest mtu*/ + *stat_p = mnl_attr_get_u32(attrs[i]); + } + } else { + /*bc, up and active*/ + if(i < TIPC_NLA_LINK_PROP) + { + *stat_p = attrs[i] ? 0: 1; + } + } + offset_p++; + } + /*skip unspec*/ + SET_OFFSET_AND_STEP(offset_p, 1, &(dp->samples)[index], link_props); + for ( i = TIPC_NLA_PROP_PRIO; i < __TIPC_NLA_PROP_MAX; i++) { + stat_p = (uint32_t *)(uintptr_t)*offset_p; + stat_p += stat_index; + *stat_p = mnl_attr_get_u32(prop[i]); + offset_p++; + } + /*skip unspec*/ + SET_OFFSET_AND_STEP(offset_p, 1, &(dp->samples)[index], stat_info); + for ( i = TIPC_NLA_STATS_RX_INFO; i < __TIPC_NLA_STATS_MAX; i++) { + stat_p = (uint32_t *)(uintptr_t)*offset_p; + last_p = &stat_p[l0_w_conf.nos]; + stat_p += stat_index; + + if(dp->counter[index] < (l0_w_conf.nos)) + { + if(stat_index) + { + *stat_p = mnl_attr_get_u32(stats[i]) - *last_p; + } else { + *stat_p = mnl_attr_get_u32(stats[i]); + } + } else { + *stat_p = mnl_attr_get_u32(stats[i]) - *last_p; + } + *last_p = mnl_attr_get_u32(stats[i]); + offset_p++; + threshold = link_stat_threshold[i]; + TIPC_ALARM(((threshold > 0) && (*stat_p > threshold)), stat_opts[i], threshold, *stat_p, name); + } + + offset_stat_info_p = (stat_information *)(uintptr_t *)((char *)&(dp->samples)[index] + offsetof(tipceLinkStatistics, stat_info)); + offset_link_info_p = (link_information *)(uintptr_t *)((char *)&(dp->samples)[index] + offsetof(tipceLinkStatistics, link_info)); + offset_link_misc_p = (link_miscellaneous *)(uintptr_t *)((char *)&(dp->samples)[index] + offsetof(tipceLinkStatistics, link_misc)); + + offset_link_misc_p->rx_packets[stat_index] = + offset_link_info_p->link_rx[stat_index] - offset_stat_info_p->rx_info[stat_index]; + threshold = link_stat_threshold_priv[PRIV_TIPC_NLA_STATS_RX_PACKETS]; + TIPC_ALARM(((threshold > 0) && (offset_link_misc_p->rx_packets[stat_index] > threshold)), + "rx_packets", threshold, offset_link_misc_p->rx_packets[stat_index], name); + + offset_link_misc_p->tx_packets[stat_index] = + offset_link_info_p->link_tx[stat_index] - offset_stat_info_p->tx_info[stat_index]; + threshold = link_stat_threshold_priv[PRIV_TIPC_NLA_STATS_TX_PACKETS]; + TIPC_ALARM(((threshold > 0) && + ( offset_link_misc_p->tx_packets[stat_index] > threshold)), + "tx_packets", threshold, offset_link_misc_p->tx_packets[stat_index], name); + + offset_link_misc_p->rx_loss[stat_index] = perc( offset_stat_info_p->rx_nacks[stat_index], + offset_link_misc_p->tx_packets[stat_index]); + threshold = link_stat_threshold_priv[PRIV_TIPC_NLA_STATS_RX_LOSS_RATE]; + TIPC_ALARM(((threshold > 0) && + (offset_link_misc_p->rx_loss[stat_index] > threshold)), + "rx_loss", threshold , offset_link_misc_p->rx_loss[stat_index], name); + + offset_link_misc_p->tx_loss[stat_index] = perc(offset_stat_info_p->tx_nacks[stat_index], + offset_link_misc_p->rx_packets[stat_index]); + threshold = link_stat_threshold_priv[PRIV_TIPC_NLA_STATS_TX_LOSS_RATE]; + TIPC_ALARM(((threshold > 0) && + (offset_link_misc_p->tx_loss[stat_index] > threshold)), + "tx_loss", threshold, offset_link_misc_p->tx_loss[stat_index], name); + + dp->counter[index]++; + return MNL_CB_OK; +} + +static int l1_unicast_sample(int index, + int uc_index, + data *uc_dp, + data *dp) +{ + int i; + uint32_t *uc_stat_p; + uint32_t *stat_p; + uintptr_t *offset_p; + uintptr_t *uc_offset_p; + + SET_OFFSET_AND_STEP(offset_p, 2, &(dp->samples)[index], link_info); + SET_OFFSET_AND_STEP(uc_offset_p, 2, &(uc_dp->samples)[uc_index], link_info); + + /* FIXME: fix the print to csv file b4 rewrite this part! + it does not make sence to calculate some of following propreties*/ + for ( i = TIPC_NLA_LINK_DEST; i < __TIPC_NLA_LINK_MAX; i++) + { + stat_p = (uint32_t *)(uintptr_t)*offset_p; + stat_p += l1_stat_index; + uc_stat_p = (uint32_t *)(uintptr_t)*uc_offset_p; + + if ((i < TIPC_NLA_LINK_BROADCAST) || (i > TIPC_NLA_LINK_STATS)) + { + /* link_rx, link_tx + * dest, mtu: should be the same value as dest and mtu values + * in layer 0 window. + */ + *stat_p = calc_mean(stat_index, l0_w_conf.nos, &uc_stat_p, first_sample[uc_index]); + } else { + /* up and active*/ + if((i < TIPC_NLA_LINK_PROP) && (i > TIPC_NLA_LINK_BROADCAST)) + /* *stat_p = calc_mean(stat_index, l0_w_conf.nos, &uc_stat_p, first_sample[uc_index]); */ + *stat_p = calc_sum(l0_w_conf.nos, uc_stat_p); + } + offset_p++; + uc_offset_p++; + } + + SET_OFFSET_AND_STEP(offset_p, 1, &(dp->samples)[index], link_props); + SET_OFFSET_AND_STEP(uc_offset_p, 1, &(uc_dp->samples)[index], link_props); + for (i = TIPC_NLA_PROP_PRIO; i < __TIPC_NLA_PROP_MAX; i++) { + stat_p = (uint32_t *)(uintptr_t)*offset_p; + stat_p += l1_stat_index; + uc_stat_p = (uint32_t *)(uintptr_t)*uc_offset_p; + *stat_p = calc_mean(stat_index, l0_w_conf.nos, &uc_stat_p, first_sample[uc_index]); + offset_p++; + uc_offset_p++; + } + SET_OFFSET_AND_STEP(offset_p, 1, &(dp->samples)[index], stat_info); + SET_OFFSET_AND_STEP(uc_offset_p, 1, &(uc_dp->samples)[index], stat_info); + for (i = TIPC_NLA_STATS_RX_INFO; i < __TIPC_NLA_STATS_MAX; i++) { + stat_p = (uint32_t *)(uintptr_t)*offset_p; + stat_p += l1_stat_index; + uc_stat_p = (uint32_t *)(uintptr_t)*uc_offset_p; + *stat_p = calc_mean(stat_index, l0_w_conf.nos, &uc_stat_p, first_sample[uc_index]); + offset_p++; + uc_offset_p++; + } + SET_OFFSET_AND_STEP(offset_p, 1, &(dp->samples)[index], link_misc); + SET_OFFSET_AND_STEP( uc_offset_p, 1, &(uc_dp->samples)[index], link_misc); + + IF_WATCH(w_linkp->action, + (look_up_link_index(uc_dp->samples[index].link_info.link, + w_linkp->action_list, w_linkp->action) >= 0), + printf("\n%s: link reset:%-10lu ", (uc_dp)->samples[index].link_info.link, (uc_dp)->samples[index].link_state.down)); + + for (i = PRIV_TIPC_NLA_STATS_RX_PACKETS; i < __PRIV_TIPC_NLA_STATS_MAX; i++) { + stat_p = (uint32_t *)(uintptr_t)*offset_p; + stat_p += l1_stat_index; + uc_stat_p = (uint32_t *)(uintptr_t)*uc_offset_p; + *stat_p = calc_mean(stat_index, l0_w_conf.nos, &uc_stat_p, first_sample[uc_index]); + /* + * Attention: There is some bug in TIPC or this code regarding values of + * rx_packets and tx_packets. + * When any link is reset, rx_packets and tx_packets are rabish values until the link + * is restablished. + */ + IF_WATCH(w_linkp->action, + (look_up_link_index(uc_dp->samples[index].link_info.link, w_linkp->action_list, w_linkp->action) >= 0), + printf("%s:%-10u ", stat_opts_priv[i], *stat_p)); + offset_p++; + uc_offset_p++; + } + dp->counter[index]++; + return MNL_CB_OK; +} + +static int parse_attrs(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + + tb[ mnl_attr_get_type(attr)] = attr; + return MNL_CB_OK; +} + +static int family_id_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[CTRL_ATTR_MAX + 1] = {}; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + int *id = data; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, tb); + if (!tb[CTRL_ATTR_FAMILY_ID]) + return MNL_CB_ERROR; + + *id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]); + return MNL_CB_OK; +} + + +static void nl_prep_msg(struct nlmsghdr **nlhp, + void *buf, + uint16_t nlmsg_type, + uint16_t nlmsg_flags, + uint16_t cmd, /* uint16_t? */ + time_t *nlSeq) +{ + /* + Allocate a buffer of MNL_SOCKET_BUFFER_SIZE + (which is 8KB, see linux/netlink.h for more information). + Using this buffer size ensures that the buffer is big enough + to store the netlink message without truncating it. + */ + + /* Pointer to Generic Netlink message header */ + struct genlmsghdr *genlh; + struct nlmsghdr *nlh; + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = nlmsg_type; + nlh->nlmsg_flags = nlmsg_flags; + + (void)time(nlSeq); + nlh->nlmsg_seq = *nlSeq; + + genlh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr)); + genlh->cmd = cmd; + genlh->version = 1; + *nlhp = nlh; + return; +} + +static void nl_send_msg(struct mnl_socket **nls, + struct nlmsghdr *nlh, + int nlSocket, + pid_t portId) +{ + /* R we getting the right errno when mnl_*_* funcs returns?!*/ + if ((*nls = mnl_socket_open(nlSocket) ) == NULL) + { + syslog(LOG_ERR, "mnl_socket_open: Failed, %s", strerror(errno)); + exit(-1); + } + + if (mnl_socket_bind(*nls, 0, portId) < 0) + { + mnl_socket_close(*nls); + syslog(LOG_ERR, "mnl_socket_bind: Failed, %s", strerror(errno)); + exit(-1); + } + + /* Maybe check return value equal to nlh->nlmsg_len? */ + if (mnl_socket_sendto(*nls, nlh, nlh->nlmsg_len) < 0) + { + mnl_socket_close(*nls); + syslog(LOG_ERR, "mnl_socket_sendto: Failed, %s", strerror(errno)); + exit(-1); + } + return; +} + +static void nl_rcv_msg(struct mnl_socket *nls, + mnl_cb_t cb, + void *cb_data, + time_t *nlSeq) +{ + int ret; + char buf[MNL_SOCKET_BUFFER_SIZE]; + uint32_t portId = mnl_socket_get_portid(nls); + + if ((ret = mnl_socket_recvfrom(nls, buf, sizeof(buf))) < 0) + { + mnl_socket_close(nls); + syslog(LOG_ERR, "mnl_socket_recvfrom: Failed, %s", strerror(errno)); + exit(-1); + } + + while (ret > 0) { + ret = mnl_cb_run(buf, + ret, + (uint32_t)*nlSeq, + portId, + cb, + cb_data); + if (ret <= 0) { + if (ret == MNL_CB_ERROR) + { + mnl_socket_close(nls); + syslog(LOG_ERR, "mnl_cb_run: Failed, (MNL_CB_ERROR) %s", strerror(errno)); + exit(-1); + } + break; + } + + if ((ret = mnl_socket_recvfrom(nls, buf, sizeof(buf))) < 0) + { + mnl_socket_close(nls); + syslog(LOG_ERR, "mnl_socket_recvfrom: Failed, %s", strerror(errno)); + exit(-1); + } + } + return; +} + +static int get_tipc_family(void) +{ + int nlFamily; + time_t nlSeq; + struct mnl_socket *nls; + struct nlmsghdr *nlh; + char buf[MNL_SOCKET_BUFFER_SIZE]; + + nl_prep_msg(&nlh, + buf, + GENL_ID_CTRL, + NLM_F_REQUEST | NLM_F_ACK, + CTRL_CMD_GETFAMILY, + &nlSeq); + + mnl_attr_put_u32(nlh, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL); + mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, TIPC_GENL_V2_NAME); + + nl_send_msg(&nls, + nlh, + NETLINK_GENERIC, + MNL_SOCKET_AUTOPID); + + nl_rcv_msg(nls, + family_id_cb, + &nlFamily, + &nlSeq); + mnl_socket_close(nls); + + return nlFamily; +} + +static int validate_opt(const char* str) +{ + size_t i, len = strlen(str); + + for (i = 0; i < len; i++) { + if(!isdigit(str[i])) + return 0; + } + return 1; +} + +static bool handle_getsubopt(char **optionp, char *const *tokens, + uint32_t *t_link, int t_low, int t_high) +{ + int sub_opt; + int tmp; + char *valuep; + while (*optionp != '\0') { + sub_opt = getsubopt (optionp, tokens, &valuep); + if ((t_low < sub_opt) && (sub_opt < t_high) ) + { + if ( *valuep ) + { + tmp = atoi (valuep); + if (tmp > 0) + { + t_link[sub_opt] = atoi (valuep); + return true; + } + } + fprintf(stderr, "The suboption `%s is not given any valid value!\n", + tokens[sub_opt]); + fflush(stderr); + abort (); + } + else + return false; + } + return false; +} + +static int handle_opts(int argc, char *const argv[]) +{ + + int opt; + bool found =false; + char *pName = strrchr(argv[0], '/'); + pName = pName ? 1+pName : argv[0]; + + while (EOF != (opt = getopt(argc, argv, "hdxs:a:p:f:l:n:r:o:"))) { + + switch (opt) { + case 'r': + /* Reset following link(s) */ + r_linkp->action_list[r_linkp->action] = malloc(strlen(optarg) + 1); + if (!r_linkp->action_list[r_linkp->action]) + { + syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s", + strerror(errno)); + exit(-1); + } + strncpy(r_linkp->action_list[r_linkp->action], optarg, strlen(optarg)); + r_linkp->action++; + break; + case 'l': + /* Watch only following link(s) */ + w_linkp->action_list[w_linkp->action] = malloc(strlen(optarg) + 1); + if (!w_linkp->action_list[w_linkp->action]) + { + syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s", + strerror(errno)); + exit(-1); + } + strncpy(w_linkp->action_list[w_linkp->action], optarg, strlen(optarg)); + w_linkp->action++; + break; + case 'n': + /* Watch only following nodes(s) */ + w_nodep->action_list[w_nodep->action] = malloc(strlen(optarg) + 1); + if (!w_nodep->action_list[w_nodep->action]) + { + syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s", + strerror(errno)); + exit(-1); + } + strncpy(w_nodep->action_list[w_nodep->action], optarg, strlen(optarg)); + w_nodep->action++; + break; + case 'f': + printf(" %d %s \n", __LINE__, optarg); + fflush(stdout); + user_dir = malloc(strlen(optarg) + 1); + if (!user_dir) + { + syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s", + strerror(errno)); + exit(-1); + } + strncpy(user_dir, optarg, strlen(optarg)); + printf(" %d \n", __LINE__); + fflush(stdout); + break; + case 'x': + /* Only one sample */ + l0_w_conf.one_shot = 1; + break; + case 'd': + /* Do not daemonize */ + l0_w_conf.nod = 1; + break; + case 's': + /* Number of samples, The size of sliding window level zero*/ + if(validate_opt(optarg)) + { + l0_w_conf.nos = atoi(optarg); + if(l0_w_conf.nos < 2) + { + printf("The size of the window must be gt one!\n"); + abort(); + } + } else { + printf("option requires an argument\n"); + abort(); + } + break; + case 'a': + /* The size of sliding window level one; mean of samples of window level zero*/ + if(validate_opt(optarg)) + { + l1_w_conf.nos = atoi(optarg); + if(l1_w_conf.nos < 1) + { + printf("The size of the window must be gt zero!\n"); + abort(); + } + } + else { + printf("option requires an argument\n"); + abort(); + } + break; + case 'p': + /* How often*/ + if(validate_opt(optarg)) + l0_w_conf.period = atoi(optarg); + else { + printf("option requires an argument\n"); + abort(); + } + break; + case 'o': + { + char *sub_opts; + sub_opts = optarg; + found = handle_getsubopt(&sub_opts, thresholds, + thresholds_link, + THRESHOLD_UNSPEC, + THRESHOLD_MAX); + if(!found) + { + sub_opts = optarg; + found = handle_getsubopt(&sub_opts, stat_opts_link, + link_info_threshold, + TIPC_NLA_LINK_STATS, + __TIPC_NLA_LINK_MAX); + } + if(!found) + { + sub_opts = optarg; + found = handle_getsubopt(&sub_opts, stat_opts, + link_stat_threshold, + TIPC_NLA_STATS_UNSPEC, + __TIPC_NLA_STATS_MAX); + } + if(!found) + { + sub_opts = optarg; + found = handle_getsubopt(&sub_opts, stat_opts_priv, + link_stat_threshold_priv, + PRIV_TIPC_NLA_STATS_LINK_STATE, + __PRIV_TIPC_NLA_STATS_MAX); + } + if(found) + found=false; + else { + fprintf(stderr,"Unknown suboption\n"); + abort(); + } + } + break; + case 'h': + usage(pName); + exit(0); + case ':': + case '?': + default: + usage(pName); + exit(-1); + } + } + return 0; +} + +static void topsrv_subscibe(event_status *events, int len) +{ + int i; + struct sockaddr_tipc sa = { + .family = AF_TIPC, + .addrtype = TIPC_ADDR_NAME, + .addr.name.name.type = TIPC_TOP_SRV, + .addr.name.name.instance = TIPC_TOP_SRV, + .addr.name.domain = 0 + }; + socklen_t sa_len = sizeof(sa); + + if (!(topsrv_sd = socket(AF_TIPC, SOCK_SEQPACKET, 0))) + { + syslog(LOG_ERR, "socket: Failed to open TIPC socket: %s", + strerror(errno)); + exit(-1); + } + + if ( 0 > connect(topsrv_sd, (struct sockaddr*)&sa, sizeof(sa))) + { + syslog(LOG_ERR, "connect: Failed to connect to TIPC topology server: %s", + strerror(errno)); + exit(-1); + } + + memset(&sa, 0, sizeof(sa)); + sa_len = sizeof(sa); + + if (0 > getsockname(topsrv_sd, (struct sockaddr*)&sa, &sa_len)) + { + syslog(LOG_ERR, "getsockname: Failed to get this node TIPC address: %s", + strerror(errno)); + exit(-1); + } + + this_node = sa.addr.id.node; + + for (i = 0; i < len; i++) { + + struct tipc_subscr link_sub = { + .seq.type = htonl(events[i].event), /* topology service name type */ + .seq.lower = htonl(0), + .seq.upper = htonl(~0), + /* subscription duration set 24ever*/ + .timeout = htonl(TIPC_WAIT_FOREVER), + .filter = htonl(TIPC_SUB_PORTS), + }; + strcpy(link_sub.usr_handle, events[i].resource); + if (send(topsrv_sd, &link_sub, sizeof(link_sub), 0) != sizeof(link_sub)) + { + syslog(LOG_ERR, + "Failed to subscribe to \"TIPC Link\" Or \"Node State\" event(s): %s", + strerror(errno)); + exit(-1); + } + } + return; +} + +static void setup_sample_timer(void) +{ + int err = 0; + timer_fd = timerfd_create (CLOCK_REALTIME, 0); + struct itimerspec new_value = { + /* new_value.it_value specifies the initial expiration of the timer, in + seconds and nanoseconds. Setting either field of new_value.it_value + to a nonzero value arms the timer. Setting both fields of + new_value.it_value to zero disarms the timer. + */ + + .it_value.tv_sec = 1, + .it_value.tv_nsec = 0, + + /* Setting one or both fields of new_value.it_interval to nonzero values + specifies the period, in seconds and nanoseconds, for repeated timer + expirations after the initial expiration. If both fields of + new_value.it_interval are zero, the timer expires just once, at the + time specified by new_value.it_value. + */ + + .it_interval.tv_sec = l0_w_conf.period, + .it_interval.tv_nsec = 0 + }; + + if ((err = timerfd_settime(timer_fd, 0, &new_value, 0)) < 0) + { + syslog(LOG_ERR, "timerfd_settime: Failed with error: %d", err); + exit(-1); + } + return; +} + +static void reset_tipc_stat(char *link) +{ + struct mnl_socket *nls; + struct nlmsghdr *nlh; + time_t nlSeq; + char mnl_s_buffer[MNL_SOCKET_BUFFER_SIZE]; + struct nlattr *nest; + syslog(LOG_INFO, "start to reset the link %s \n", link); + nl_prep_msg(&nlh, + mnl_s_buffer, + get_tipc_family(), + NLM_F_REQUEST | NLM_F_ACK, + TIPC_NL_LINK_RESET_STATS, + &nlSeq); + nest = mnl_attr_nest_start(nlh, TIPC_NLA_LINK); + mnl_attr_put_strz(nlh, TIPC_NLA_LINK_NAME, link); + mnl_attr_nest_end(nlh, nest); + nl_send_msg(&nls, + nlh, + NETLINK_GENERIC, + MNL_SOCKET_AUTOPID); + nl_rcv_msg(nls, + NULL, + NULL, + &nlSeq); + mnl_socket_close(nls); + syslog(LOG_INFO, "reset link %s is done \n", link); +} + +static void reset_link_statistic(void) +{ + if (r_linkp->action) + { + int r; + for (r = 0; r < r_linkp->action ; r++) { + reset_tipc_stat(r_linkp->action_list[r]); + } + } +} + +static void request_link_statistic(char *mnl_s_buffer, + struct mnl_socket **nls, + struct nlmsghdr **nlh, + time_t *nlSeq) +{ + + nl_prep_msg(nlh, + mnl_s_buffer, + get_tipc_family(), + NLM_F_REQUEST | NLM_F_DUMP, + TIPC_NL_LINK_GET, + nlSeq); + nl_send_msg(nls, + *nlh, + NETLINK_GENERIC, + MNL_SOCKET_AUTOPID); + } + +static void handle_l1_uc(void) +{ + + int i; + l1_w_conf.counter++; + + for (i = 0; i < uc_links; i++) { + int j = insert_link(uc_samples[i].link_info.link, + "L1_W", /*Not used*/ + uc_add_link, + l1_w_conf.nos, + l1_dp); + + l1_unicast_sample(j, i, uc_dp, l1_dp); + + if ( (first_sample[i] == 0) && ( (stat_index + 1) % l0_w_conf.nos ) == 0 ) + first_sample[i] = 1; + + if (l0_w_conf.window_passed) + IF_WATCH(w_linkp->action, + ((look_up_link_index(uc_dp->samples[i].link_info.link, + w_linkp->action_list, w_linkp->action)) >= 0), + link_quality(i, l1_dp, uc_dp)); + } + + if (l0_w_conf.one_shot) + l1_stat_index++; + else + l1_stat_index = ( l1_stat_index + 1 ) % l1_w_conf.nos; + + if (l1_w_conf.counter > l1_w_conf.nos) + l1_w_conf.window_passed = true; +} + +static int link_get_stat_cb(const struct nlmsghdr *nlh, void *data) +{ + const char *name; + const char *link_state; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *attrs[TIPC_NLA_MAX + 1] = {}; + struct nlattr *info[TIPC_NLA_LINK_MAX + 1] = {}; + struct nlattr *prop[TIPC_NLA_PROP_MAX + 1] = {}; + struct nlattr *stats[TIPC_NLA_STATS_MAX + 1] = {}; + int link = 0; + + mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info); + if (!info[TIPC_NLA_LINK]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(info[TIPC_NLA_LINK], parse_attrs, attrs); + + if (!attrs[TIPC_NLA_LINK_NAME] || + !attrs[TIPC_NLA_LINK_PROP] || + !attrs[TIPC_NLA_LINK_STATS]) + return MNL_CB_ERROR; + + mnl_attr_parse_nested(attrs[TIPC_NLA_LINK_PROP], parse_attrs, prop); + mnl_attr_parse_nested(attrs[TIPC_NLA_LINK_STATS], parse_attrs, stats); + + name = mnl_attr_get_str(attrs[TIPC_NLA_LINK_NAME]); + + if (attrs[TIPC_NLA_LINK_BROADCAST]) + return 1; + + if (attrs[TIPC_NLA_LINK_ACTIVE]) + link_state = "ACTIVE"; + else if (attrs[TIPC_NLA_LINK_UP]) + link_state = "STANDBY"; + else + link_state = "DEFUNCT"; + + link = insert_link(name, link_state, uc_add_link, l0_w_conf.nos, uc_dp); + + return unicast_sample(attrs, prop, + stats, link, + uc_dp, name); +} + +static void handle_statistic_fd(struct mnl_socket **nls, + time_t **nlSeq) +{ + static int counter=0; + + nl_rcv_msg(*nls, link_get_stat_cb, NULL, *nlSeq); + counter++; + printf("Total # of samples: %d \n", counter); + if( counter > ( l0_w_conf.nos - 1 )) + { + l0_w_conf.window_passed = true; + handle_l1_uc(); + } + else + { + /*skip so far*/ + ; + } +} + +static void handle_topsrv_sd(struct tipc_event *tipc_evt) +{ + + if (recv(topsrv_sd, tipc_evt, sizeof(*tipc_evt), 0) == sizeof(*tipc_evt)) + { + switch (ntohl((*tipc_evt).s.seq.type)) + { + case TIPC_CFG_SRV: + if (ntohl(tipc_evt->port.node) == this_node) + return; + /* fall through*/ + case TIPC_LINK_STATE: + log_event(topsrv_sd, tipc_evt); + break; + default: + syslog(LOG_WARNING, "Unknown event received: %d", (*tipc_evt).s.seq.type); + break; + } + } +} + +static void handle_select(time_t *nlSeq, + struct nlmsghdr **nlh, + struct mnl_socket *nls) +{ + int link, err = 0; + fd_set fds; + struct tipc_event tipc_evt = {0}; + time_t the_time = time(NULL); + /* If threadsafe is wished, use localtime_r*/ + struct tm *tm = localtime(&the_time); + strftime(start_time, sizeof(start_time),"%Y-%B-%d:%H:%M:%S",tm); + stat_index = 0; + l1_stat_index = 0; + + while (stat_index < l0_w_conf.nos ) { + FD_ZERO(&fds); + FD_SET((nls)->fd, &fds); + FD_SET(timer_fd, &fds); + FD_SET(topsrv_sd, &fds); + if ((err = select(FD_SETSIZE, &fds, NULL, NULL, NULL)) < 0) + { + if ( err == -1 && errno == EINTR) + continue; + syslog(LOG_ERR, "select: Failed, %s", strerror(errno)); + exit(-1); + } + /* Accept only one print at a time, dirty?*/ + if(take_sample) + { + handle_print_to_file(); + take_sample = false; + } + if (FD_ISSET((nls)->fd, &fds)) + { + printf("\033[2J\033[;H"); + + handle_statistic_fd(&nls, + &nlSeq); + + if (l0_w_conf.one_shot) + { + stat_index++; + } + else + stat_index = (stat_index + 1) % l0_w_conf.nos; + } + + if (FD_ISSET(topsrv_sd, &fds)) + { + handle_topsrv_sd(&tipc_evt); + } + + if (FD_ISSET(timer_fd, &fds)) + { + /* + If the timer has expired one or more times since its + settings were last modified using timerfd_settime(), or since + the last successful read(2), then the buffer given to read(2) + returns an unsigned 8-byte integer (uint64_t) containing the + number of expirations that have occurred. + Do this dummy read to get select happy!!! + */ + uint64_t dummy; + if (read(timer_fd, &dummy, sizeof(dummy)) < 0) { + syslog(LOG_ERR, "read: Failed to read %d, %s", + timer_fd, strerror(errno)); + exit(-1); + } + + if (mnl_socket_sendto(nls, *nlh, (*nlh)->nlmsg_len) < 0) + { + syslog(LOG_ERR, "mnl_socket_sendto: Failed, %s", strerror(errno)); + exit(-1); + } + + for (link = 0; link < uc_links; link++) { + if ((uc_dp->samples)[link].link_state.count_down) + { + (uc_dp->samples)[link].link_state.count_down--; + break; + } + } + } + } +} + +/*FIXME: call the function when process terminats with non zero exit()*/ +static void free_resources(struct mnl_socket *nls) +{ + if (nls) + mnl_socket_close(nls); + if (topsrv_sd) + shutdown(topsrv_sd, SHUT_RDWR); + if (uc_samples) + free(uc_samples); + if (uc_tot_mem) + free(uc_tot_mem); + //if(LOCKFILE) /*FIXME the check of lockfile*/ + unlink(LOCKFILE); +} + +/* Thank you a lots W. Richie S.! */ +int main(int argc, char *argv[]) +{ + struct mnl_socket *nls; + struct nlmsghdr *nlh; + char mnl_s_buffer[MNL_SOCKET_BUFFER_SIZE]; + time_t nlSeq; + char *cmd = strrchr(argv[0], '/'); + cmd = cmd ? 1+cmd : argv[0]; + + handle_opts(argc, argv); + + daemonize_me(cmd); + + syslog(LOG_INFO, "The TIPC Network Daemon has started"); + + printf("\033[2J\033[;H"); + + handle_signals(); + /* Subsrcibe for all link and node events! No Zero div problem?!*/ + topsrv_subscibe(tipc_events, sizeof(tipc_events)/sizeof(tipc_events[0])); + + setup_sample_timer(); + + reset_link_statistic(); + + request_link_statistic(mnl_s_buffer, &nls, &nlh, &nlSeq); + + handle_select(&nlSeq, &nlh, nls); + + handle_print_to_file(); + + free_resources(nls); + syslog(LOG_INFO, "The TIPC Network Daemon has terminated"); + exit(0); +} -- 2.1.4 2017-06-08 10:17 GMT+02:00 Ying Xue <[email protected]>: > Hi, > > Interesting! > Where did you post your code? > > Thanks, > Ying > > On 06/08/2017 03:51 PM, Akbar Rezvan wrote: > > Hello, > > > > This code is a result of a network fault detection study , where tipc > > network is part of it. The code > > > > was written as a prototype to confirm that the concept of tipc network > > fault detection described in the study actually works. > > > > The daemon can be run in daemon mode or in foreground mode. In foreground > > mode that you can watch tipc link status live. > > > > And user is able to dump samples of statistics provided by tipc at any > time. > > > > > > kindly > > > > Akbar Anthony > > ------------------------------------------------------------ > ------------------ > > Check out the vibrant tech community on one of the world's most > > engaging tech sites, Slashdot.org! http://sdm.link/slashdot > > _______________________________________________ > > tipc-discussion mailing list > > [email protected] > > https://lists.sourceforge.net/lists/listinfo/tipc-discussion > > > ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ tipc-discussion mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/tipc-discussion
