--- /dev/null
+/*
+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;
+}