X-Git-Url: http://git.tdb.fi/?p=netmon.git;a=blobdiff_plain;f=main.c;fp=main.c;h=472c83c82dc48d210139555bf7534d0e69745269;hp=fd2e8789148735853ac4c954fda0466d5a78687d;hb=bf1a831e4606ffb150e5ad961360b74dab54dfdb;hpb=ea00f37d541ba2c75a4a33c5d37c9ae15dac5ab7 diff --git a/main.c b/main.c index fd2e878..472c83c 100644 --- 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 #include #include +#include +#include +#include +#include +#include +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(optindh_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