]> git.tdb.fi Git - netmon.git/blob - main.c
Current version
[netmon.git] / main.c
1 /*
2 netmon - a simple network connectivity monitor
3 Copyright © 2008 Mikko Rasa, Mikkosoft Productions
4 Distributed under the GPL
5 */
6
7 #include <stdint.h>
8 #include <stdio.h>
9 #include <getopt.h>
10 #include <errno.h>
11 #include <syslog.h>
12 #include <sys/poll.h>
13 #include <netinet/ip_icmp.h>
14 #include <netdb.h>
15 #include <sys/wait.h>
16 #include <stdlib.h>
17
18 unsigned checksum(const char *, unsigned);
19 void send_ping(in_addr_t, uint16_t);
20 pid_t run_command(const char *);
21
22 int sock;
23 int pid;
24
25 int main(int argc, char **argv)
26 {
27         const char *target_name = "google.com";
28         struct hostent *target_host;
29         in_addr_t target;
30         struct pollfd pfd;
31         uint16_t seq = 1;
32         uint16_t pending = 0;
33         unsigned lost = 0;
34         unsigned streak = 0;
35         unsigned max_streak = 0;
36         unsigned next_stats = 0;
37         int o;
38         int no_daemon = 0;
39         int verbose = 0;
40         unsigned stats_interval = 1800;
41         unsigned trigger_count = 900;
42         const char *trigger_cmd = NULL;
43         unsigned next_trigger;
44         pid_t child_pid = 0;
45
46         /* Parse options */
47         while((o = getopt(argc, argv, "fvi:t:c:"))!=-1)
48                 switch(o)
49                 {
50                 case 'f':
51                         no_daemon = 1;
52                         break;
53                 case 'v':
54                         verbose = 1;
55                         break;
56                 case 'i':
57                         stats_interval = strtoul(optarg, NULL, 10);
58                         break;
59                 case 't':
60                         trigger_count = strtoul(optarg, NULL, 10);
61                         break;
62                 case 'c':
63                         trigger_cmd = optarg;
64                         break;
65                 }
66
67         /* Take target hostname from commandline if specified */
68         if(optind<argc)
69                 target_name = argv[optind];
70
71         target_host = gethostbyname(target_name);
72         if(!target_host)
73         {
74                 herror("gethostbyname");
75                 return 1;
76         }
77
78         if(target_host->h_addrtype!=AF_INET)
79         {
80                 fprintf(stderr, "Got a hostent, but it doesn't have an IPv4 address");
81                 return 1;
82         }
83         target = ntohl(*(in_addr_t *)*target_host->h_addr_list);
84
85         if(verbose)
86                 printf("Target is %s\n", inet_ntoa(htonl(target)));
87
88         /* Miscellaneous initialization */
89         pid = getpid();
90         sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
91         if(sock==-1)
92         {
93                 perror("socket");
94                 return 1;
95         }
96
97         pfd.fd = sock;
98         pfd.events = POLLIN;
99
100         if(!no_daemon && daemon(1, 0)==-1)
101                 perror("daemon");
102
103         openlog("netmon", 0, LOG_LOCAL7);
104
105         next_trigger = trigger_count;
106         while(1)
107         {
108                 struct timeval tv;
109                 int res;
110
111                 gettimeofday(&tv, NULL);
112
113                 /* Send stats to syslog every stats_interval seconds */
114                 if(tv.tv_sec>next_stats)
115                 {
116                         if(next_stats)
117                         {
118                                 if(no_daemon)
119                                         printf("Lost %d, max streak %d\n", lost, max_streak);
120                                 else
121                                         syslog(LOG_INFO, "Lost %d, max streak %d", lost, max_streak);
122                                 lost = 0;
123                                 max_streak = 0;
124                         }
125                         next_stats = tv.tv_sec+stats_interval;
126                 }
127
128                 if(child_pid)
129                 {
130                         if(waitpid(child_pid, NULL, WNOHANG)==child_pid)
131                                 child_pid = 0;
132                 }
133
134                 res = poll(&pfd, 1, 1000);
135                 if(res>0)
136                 {
137                         struct sockaddr_in addr;
138                         socklen_t alen = sizeof(addr);
139                         char data[1500];
140                         int len;
141                         struct iphdr *ip;
142                         struct icmphdr *icmp;
143
144                         /* Receive a packet */
145                         len = recvfrom(sock, data, sizeof(data), 0, (struct sockaddr *)&addr, &alen);
146                         if(len==-1)
147                                 fprintf(stderr, "recvfrom error: %s\n", strerror(errno));
148                         else
149                         {
150                                 ip = (struct iphdr *)data;
151                                 if(ip->protocol==IPPROTO_ICMP)
152                                 {
153                                         icmp = (struct icmphdr *)(ip+1);
154                                         if(icmp->type==ICMP_ECHOREPLY && icmp->un.echo.id==pid)
155                                         {
156                                                 /* It's an ICMP echo reply and ours, process it */
157                                                 if(verbose)
158                                                         printf("Ping reply from %s\n", inet_ntoa(addr.sin_addr.s_addr));
159                                                 if(icmp->un.echo.sequence==pending)
160                                                 {
161                                                         pending = 0;
162                                                         streak = 0;
163                                                         next_trigger = trigger_count;
164                                                 }
165                                                 else if(verbose)
166                                                         printf("Sequence %d, expected %d\n", icmp->un.echo.sequence, pending);
167                                         }
168                                 }
169                         }
170                 }
171                 else
172                 {
173                         /* Poll timed out, check for lost ping and send more */
174                         if(pending)
175                         {
176                                 if(verbose)
177                                         printf("Lost ping\n");
178                                 ++lost;
179                                 ++streak;
180                                 if(streak>max_streak)
181                                         max_streak = streak;
182                                 if(streak>=next_trigger && trigger_cmd && !child_pid)
183                                 {
184                                         if(no_daemon)
185                                                 printf("Running %s\n", trigger_cmd);
186                                         else
187                                                 syslog(LOG_INFO, "Running %s\n", trigger_cmd);
188                                         child_pid = run_command(trigger_cmd);
189                                         next_trigger += trigger_count;
190                                 }
191                         }
192                         send_ping(target, seq);
193                         pending = seq;
194                         if(!++seq)
195                                 ++seq;
196                 }
197         }
198
199         closelog();
200
201         return 0;
202 }
203
204 unsigned checksum(const char *data, unsigned len)
205 {
206         unsigned        sum = 0;
207         unsigned i;
208
209         for(i=0; i<len; i+=2)
210                 sum += *(const unsigned short *)(data+i);
211         while(sum>0xFFFF)
212                 sum = (sum>>16)+(sum&0xFFFF);
213
214         return ~sum;
215 }
216
217 void send_ping(in_addr_t target, uint16_t seq)
218 {
219         char data[64];
220         unsigned i;
221         struct icmphdr *hdr;
222         struct sockaddr_in addr;
223
224         for(i=0; i<sizeof(data); ++i)
225                 data[i] = i;
226
227         hdr = (struct icmphdr *)data;
228         hdr->type = ICMP_ECHO;
229         hdr->code = 0;
230         hdr->checksum = 0;
231         hdr->un.echo.id = pid;
232         hdr->un.echo.sequence = seq;
233         hdr->checksum = checksum(data, sizeof(data));
234
235         addr.sin_family = AF_INET;
236         addr.sin_addr.s_addr = htonl(target);
237         addr.sin_port = 0;
238
239         if(sendto(sock, data, sizeof(data), 0, (struct sockaddr *)&addr, sizeof(addr))==-1)
240                 fprintf(stderr, "sendto error: %s\n", strerror(errno));
241 }
242
243 pid_t run_command(const char *cmd)
244 {
245         pid_t pid;
246
247         pid = fork();
248         if(pid==0)
249         {
250                 const char *argv[2];
251                 argv[0] = cmd;
252                 argv[1] = 0;
253                 execv(cmd, argv);
254                 exit(127);
255         }
256         else
257                 return pid;
258 }