]> git.tdb.fi Git - netmon.git/commitdiff
Redesign the program to use network statistics
authorMikko Rasa <tdb@tdb.fi>
Thu, 7 Jan 2016 19:49:53 +0000 (21:49 +0200)
committerMikko Rasa <tdb@tdb.fi>
Thu, 7 Jan 2016 19:49:53 +0000 (21:49 +0200)
This method can leverage any traffic that passes through the network
interface.  Ping is still used as well but only for reporting packet
loss.

main.c

diff --git a/main.c b/main.c
index fd2e8789148735853ac4c954fda0466d5a78687d..472c83c82dc48d210139555bf7534d0e69745269 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1,6 +1,6 @@
 /*
 netmon - a simple network connectivity monitor
-Copyright © 2008 Mikko Rasa, Mikkosoft Productions
+Copyright © 2008-2016 Mikko Rasa, Mikkosoft Productions
 Distributed under the GPL
 */
 
@@ -14,7 +14,13 @@ Distributed under the GPL
 #include <netdb.h>
 #include <sys/wait.h>
 #include <stdlib.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <string.h>
 
+unsigned long long read_number(const char *);
 unsigned checksum(const char *, unsigned);
 void send_ping(in_addr_t, uint16_t);
 pid_t run_command(const char *);
@@ -24,27 +30,35 @@ int pid;
 
 int main(int argc, char **argv)
 {
-       const char *target_name = "google.com";
-       struct hostent *target_host;
-       in_addr_t target;
+       const char *ping_target_name = NULL;
+       in_addr_t ping_target = 0;
+       const char *interface = NULL;
        struct pollfd pfd;
+       unsigned next_ping = 0;
        uint16_t seq = 1;
        uint16_t pending = 0;
+       unsigned ping_count = 0;
        unsigned lost = 0;
-       unsigned streak = 0;
-       unsigned max_streak = 0;
        unsigned next_stats = 0;
        int o;
+       char *endp;
        int no_daemon = 0;
        int verbose = 0;
        unsigned stats_interval = 1800;
-       unsigned trigger_count = 900;
+       unsigned trigger_delay = 60;
+       unsigned trigger_interval = 600;
        const char *trigger_cmd = NULL;
        unsigned next_trigger;
        pid_t child_pid = 0;
+       unsigned long long rx_packets = 0;
+       unsigned long long tx_packets = 0;
+       unsigned last_receive = 0;
+       unsigned last_transmit = 0;
+       int connection_status = 1;
+       unsigned connection_down_time;
 
        /* Parse options */
-       while((o = getopt(argc, argv, "fvi:t:c:"))!=-1)
+       while((o = getopt(argc, argv, "fvs:t:c:i:p:"))!=-1)
                switch(o)
                {
                case 'f':
@@ -53,37 +67,48 @@ int main(int argc, char **argv)
                case 'v':
                        verbose = 1;
                        break;
-               case 'i':
+               case 's':
                        stats_interval = strtoul(optarg, NULL, 10);
                        break;
                case 't':
-                       trigger_count = strtoul(optarg, NULL, 10);
+                       trigger_delay = strtoul(optarg, &endp, 10);
+                       if(*endp==',')
+                               trigger_interval = strtoul(endp+1, NULL, 10);
+                       else
+                               trigger_interval = trigger_delay;
                        break;
                case 'c':
                        trigger_cmd = optarg;
                        break;
+               case 'i':
+                       interface = optarg;
+                       break;
+               case 'p':
+                       ping_target_name = optarg;
+                       break;
                }
 
-       /* Take target hostname from commandline if specified */
-       if(optind<argc)
-               target_name = argv[optind];
-
-       target_host = gethostbyname(target_name);
-       if(!target_host)
+       if(ping_target_name)
        {
-               herror("gethostbyname");
-               return 1;
-       }
+               struct hostent *ping_target_host;
 
-       if(target_host->h_addrtype!=AF_INET)
-       {
-               fprintf(stderr, "Got a hostent, but it doesn't have an IPv4 address");
-               return 1;
-       }
-       target = ntohl(*(in_addr_t *)*target_host->h_addr_list);
+               ping_target_host = gethostbyname(ping_target_name);
+               if(!ping_target_host)
+               {
+                       herror("gethostbyname");
+                       return 1;
+               }
+
+               if(ping_target_host->h_addrtype!=AF_INET)
+               {
+                       fprintf(stderr, "Got a hostent, but it doesn't have an IPv4 address");
+                       return 1;
+               }
+               ping_target = *(in_addr_t *)*ping_target_host->h_addr_list;
 
-       if(verbose)
-               printf("Target is %s\n", inet_ntoa(htonl(target)));
+               if(verbose)
+                       printf("Ping target is %s\n", inet_ntoa(*(struct in_addr *)*ping_target_host->h_addr_list));
+       }
 
        /* Miscellaneous initialization */
        pid = getpid();
@@ -102,29 +127,104 @@ int main(int argc, char **argv)
 
        openlog("netmon", 0, LOG_LOCAL7);
 
-       next_trigger = trigger_count;
+       next_trigger = trigger_delay;
        while(1)
        {
                struct timeval tv;
                int res;
+               char fnbuf[256];
+               unsigned long long count;
 
                gettimeofday(&tv, NULL);
 
-               /* Send stats to syslog every stats_interval seconds */
-               if(tv.tv_sec>next_stats)
+               /* Read network statistics */
+               snprintf(fnbuf, sizeof(fnbuf), "/sys/class/net/%s/statistics/rx_packets", interface);
+               count = read_number(fnbuf);
+               if(count>rx_packets)
+                       last_receive = tv.tv_sec;
+               rx_packets = count;
+
+               snprintf(fnbuf, sizeof(fnbuf), "/sys/class/net/%s/statistics/tx_packets", interface);
+               count = read_number(fnbuf);
+               if(count>tx_packets)
+                       last_transmit = tv.tv_sec;
+               tx_packets = count;
+
+               /* If packets have been transmitted more recently than received, there
+               might be a problem */
+               if(last_transmit>last_receive)
                {
-                       if(next_stats)
+                       if(connection_status && tv.tv_sec>=last_receive+10)
                        {
+                               connection_status = 0;
+                               connection_down_time = last_receive;
                                if(no_daemon)
-                                       printf("Lost %d, max streak %d\n", lost, max_streak);
+                                       printf("Connection is down\n");
                                else
-                                       syslog(LOG_INFO, "Lost %d, max streak %d", lost, max_streak);
-                               lost = 0;
-                               max_streak = 0;
+                                       syslog(LOG_INFO, "Connection is down");
+                       }
+
+                       if(trigger_cmd && tv.tv_sec>last_receive+next_trigger)
+                       {
+                               if(no_daemon)
+                                       printf("Running %s\n", trigger_cmd);
+                               else
+                                       syslog(LOG_INFO, "Running %s", trigger_cmd);
+                               child_pid = run_command(trigger_cmd);
+                               next_trigger += trigger_interval;
+                       }
+               }
+               else if(!connection_status)
+               {
+                       unsigned duration = tv.tv_sec-connection_down_time;
+                       connection_status = 1;
+                       if(no_daemon)
+                               printf("Connection is up (was down for %d seconds)\n", duration);
+                       else
+                               syslog(LOG_INFO, "Connection is up (was down for %d seconds)", duration);
+
+                       next_trigger = trigger_delay;
+               }
+
+               if(ping_target)
+               {
+                       /* Send ping packets to monitor packet loss */
+                       if(tv.tv_sec>=next_ping)
+                       {
+                               if(pending)
+                               {
+                                       ++lost;
+                                       if(verbose)
+                                               printf("Lost ping\n");
+                               }
+
+                               send_ping(ping_target, seq);
+                               pending = seq++;
+                               if(!seq)
+                                       ++seq;
+                               ++ping_count;
+                               next_ping = tv.tv_sec+1;
+                       }
+
+                       if(tv.tv_sec>=next_stats)
+                       {
+                               if(lost)
+                               {
+                                       float loss_ratio = (float)lost/ping_count;
+                                       if(no_daemon)
+                                               printf("Packet loss: %.2f%%\n", loss_ratio*100);
+                                       else
+                                               syslog(LOG_INFO, "Packet loss: %.2f%%", loss_ratio*100);
+
+                                       ping_count = 0;
+                                       lost = 0;
+                               }
+
+                               next_stats = tv.tv_sec+stats_interval;
                        }
-                       next_stats = tv.tv_sec+stats_interval;
                }
 
+               /* Reap any finished child process */
                if(child_pid)
                {
                        if(waitpid(child_pid, NULL, WNOHANG)==child_pid)
@@ -155,45 +255,15 @@ int main(int argc, char **argv)
                                        {
                                                /* It's an ICMP echo reply and ours, process it */
                                                if(verbose)
-                                                       printf("Ping reply from %s\n", inet_ntoa(addr.sin_addr.s_addr));
+                                                       printf("Ping reply from %s\n", inet_ntoa(addr.sin_addr));
                                                if(icmp->un.echo.sequence==pending)
-                                               {
                                                        pending = 0;
-                                                       streak = 0;
-                                                       next_trigger = trigger_count;
-                                               }
                                                else if(verbose)
                                                        printf("Sequence %d, expected %d\n", icmp->un.echo.sequence, pending);
                                        }
                                }
                        }
                }
-               else
-               {
-                       /* Poll timed out, check for lost ping and send more */
-                       if(pending)
-                       {
-                               if(verbose)
-                                       printf("Lost ping\n");
-                               ++lost;
-                               ++streak;
-                               if(streak>max_streak)
-                                       max_streak = streak;
-                               if(streak>=next_trigger && trigger_cmd && !child_pid)
-                               {
-                                       if(no_daemon)
-                                               printf("Running %s\n", trigger_cmd);
-                                       else
-                                               syslog(LOG_INFO, "Running %s\n", trigger_cmd);
-                                       child_pid = run_command(trigger_cmd);
-                                       next_trigger += trigger_count;
-                               }
-                       }
-                       send_ping(target, seq);
-                       pending = seq;
-                       if(!++seq)
-                               ++seq;
-               }
        }
 
        closelog();
@@ -201,6 +271,26 @@ int main(int argc, char **argv)
        return 0;
 }
 
+unsigned long long read_number(const char *fn)
+{
+       char buf[64];
+       int fd;
+       int len;
+
+       fd = open(fn, O_RDONLY);
+       if(fd<0)
+               return 0;
+
+       len = read(fd, buf, sizeof(buf));
+       close(fd);
+       if(len<0)
+               return 0;
+
+       buf[len] = 0;
+
+       return strtoull(buf, NULL, 10);
+}
+
 unsigned checksum(const char *data, unsigned len)
 {
        unsigned        sum = 0;
@@ -233,7 +323,7 @@ void send_ping(in_addr_t target, uint16_t seq)
        hdr->checksum = checksum(data, sizeof(data));
 
        addr.sin_family = AF_INET;
-       addr.sin_addr.s_addr = htonl(target);
+       addr.sin_addr.s_addr = target;
        addr.sin_port = 0;
 
        if(sendto(sock, data, sizeof(data), 0, (struct sockaddr *)&addr, sizeof(addr))==-1)
@@ -250,7 +340,7 @@ pid_t run_command(const char *cmd)
                const char *argv[2];
                argv[0] = cmd;
                argv[1] = 0;
-               execv(cmd, argv);
+               execv(cmd, (char *const *)argv);
                exit(127);
        }
        else