From ea00f37d541ba2c75a4a33c5d37c9ae15dac5ab7 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Thu, 7 Jan 2016 16:05:12 +0200 Subject: [PATCH] Current version --- main.c | 258 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 main.c diff --git a/main.c b/main.c new file mode 100644 index 0000000..fd2e878 --- /dev/null +++ b/main.c @@ -0,0 +1,258 @@ +/* +netmon - a simple network connectivity monitor +Copyright © 2008 Mikko Rasa, Mikkosoft Productions +Distributed under the GPL +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +unsigned checksum(const char *, unsigned); +void send_ping(in_addr_t, uint16_t); +pid_t run_command(const char *); + +int sock; +int pid; + +int main(int argc, char **argv) +{ + const char *target_name = "google.com"; + struct hostent *target_host; + in_addr_t target; + struct pollfd pfd; + uint16_t seq = 1; + uint16_t pending = 0; + unsigned lost = 0; + unsigned streak = 0; + unsigned max_streak = 0; + unsigned next_stats = 0; + int o; + int no_daemon = 0; + int verbose = 0; + unsigned stats_interval = 1800; + unsigned trigger_count = 900; + const char *trigger_cmd = NULL; + unsigned next_trigger; + pid_t child_pid = 0; + + /* Parse options */ + while((o = getopt(argc, argv, "fvi:t:c:"))!=-1) + switch(o) + { + case 'f': + no_daemon = 1; + break; + case 'v': + verbose = 1; + break; + case 'i': + stats_interval = strtoul(optarg, NULL, 10); + break; + case 't': + trigger_count = strtoul(optarg, NULL, 10); + break; + case 'c': + trigger_cmd = 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); + + if(verbose) + printf("Target is %s\n", inet_ntoa(htonl(target))); + + /* Miscellaneous initialization */ + pid = getpid(); + sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); + if(sock==-1) + { + perror("socket"); + return 1; + } + + pfd.fd = sock; + pfd.events = POLLIN; + + if(!no_daemon && daemon(1, 0)==-1) + perror("daemon"); + + openlog("netmon", 0, LOG_LOCAL7); + + next_trigger = trigger_count; + while(1) + { + struct timeval tv; + int res; + + gettimeofday(&tv, NULL); + + /* Send stats to syslog every stats_interval seconds */ + if(tv.tv_sec>next_stats) + { + if(next_stats) + { + if(no_daemon) + printf("Lost %d, max streak %d\n", lost, max_streak); + else + syslog(LOG_INFO, "Lost %d, max streak %d", lost, max_streak); + lost = 0; + max_streak = 0; + } + next_stats = tv.tv_sec+stats_interval; + } + + if(child_pid) + { + if(waitpid(child_pid, NULL, WNOHANG)==child_pid) + child_pid = 0; + } + + res = poll(&pfd, 1, 1000); + if(res>0) + { + struct sockaddr_in addr; + socklen_t alen = sizeof(addr); + char data[1500]; + int len; + struct iphdr *ip; + struct icmphdr *icmp; + + /* Receive a packet */ + len = recvfrom(sock, data, sizeof(data), 0, (struct sockaddr *)&addr, &alen); + if(len==-1) + fprintf(stderr, "recvfrom error: %s\n", strerror(errno)); + else + { + ip = (struct iphdr *)data; + if(ip->protocol==IPPROTO_ICMP) + { + icmp = (struct icmphdr *)(ip+1); + if(icmp->type==ICMP_ECHOREPLY && icmp->un.echo.id==pid) + { + /* 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)); + 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(); + + return 0; +} + +unsigned checksum(const char *data, unsigned len) +{ + unsigned sum = 0; + unsigned i; + + for(i=0; i0xFFFF) + sum = (sum>>16)+(sum&0xFFFF); + + return ~sum; +} + +void send_ping(in_addr_t target, uint16_t seq) +{ + char data[64]; + unsigned i; + struct icmphdr *hdr; + struct sockaddr_in addr; + + for(i=0; itype = ICMP_ECHO; + hdr->code = 0; + hdr->checksum = 0; + hdr->un.echo.id = pid; + hdr->un.echo.sequence = seq; + hdr->checksum = checksum(data, sizeof(data)); + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(target); + addr.sin_port = 0; + + if(sendto(sock, data, sizeof(data), 0, (struct sockaddr *)&addr, sizeof(addr))==-1) + fprintf(stderr, "sendto error: %s\n", strerror(errno)); +} + +pid_t run_command(const char *cmd) +{ + pid_t pid; + + pid = fork(); + if(pid==0) + { + const char *argv[2]; + argv[0] = cmd; + argv[1] = 0; + execv(cmd, argv); + exit(127); + } + else + return pid; +} -- 2.43.0