]> git.tdb.fi Git - netmon.git/commitdiff
Current version
authorMikko Rasa <tdb@tdb.fi>
Thu, 7 Jan 2016 14:05:12 +0000 (16:05 +0200)
committerMikko Rasa <tdb@tdb.fi>
Thu, 7 Jan 2016 14:05:12 +0000 (16:05 +0200)
main.c [new file with mode: 0644]

diff --git a/main.c b/main.c
new file mode 100644 (file)
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 <stdint.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <errno.h>
+#include <syslog.h>
+#include <sys/poll.h>
+#include <netinet/ip_icmp.h>
+#include <netdb.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+
+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(optind<argc)
+               target_name = argv[optind];
+
+       target_host = gethostbyname(target_name);
+       if(!target_host)
+       {
+               herror("gethostbyname");
+               return 1;
+       }
+
+       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);
+
+       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; i<len; i+=2)
+               sum += *(const unsigned short *)(data+i);
+       while(sum>0xFFFF)
+               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; i<sizeof(data); ++i)
+               data[i] = i;
+
+       hdr = (struct icmphdr *)data;
+       hdr->type = 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;
+}