2 netmon - a simple network connectivity monitor
3 Copyright © 2008-2016 Mikko Rasa, Mikkosoft Productions
4 Distributed under the GPL
13 #include <netinet/ip_icmp.h>
17 #include <arpa/inet.h>
23 unsigned long long read_number(const char *);
24 unsigned checksum(const char *, unsigned);
25 void send_ping(in_addr_t, uint16_t);
26 pid_t run_command(const char *);
31 int main(int argc, char **argv)
33 const char *ping_target_name = NULL;
34 in_addr_t ping_target = 0;
35 const char *interface = NULL;
37 unsigned next_ping = 0;
40 unsigned ping_count = 0;
42 unsigned next_stats = 0;
47 unsigned stats_interval = 1800;
48 unsigned trigger_delay = 60;
49 unsigned trigger_interval = 600;
50 const char *trigger_cmd = NULL;
51 unsigned next_trigger;
53 unsigned long long rx_packets = 0;
54 unsigned long long tx_packets = 0;
55 unsigned last_receive = 0;
56 unsigned last_transmit = 0;
57 int connection_status = 1;
58 unsigned connection_down_time;
61 while((o = getopt(argc, argv, "fvs:t:c:i:p:"))!=-1)
71 stats_interval = strtoul(optarg, NULL, 10);
74 trigger_delay = strtoul(optarg, &endp, 10);
76 trigger_interval = strtoul(endp+1, NULL, 10);
78 trigger_interval = trigger_delay;
87 ping_target_name = optarg;
93 struct hostent *ping_target_host;
95 ping_target_host = gethostbyname(ping_target_name);
98 herror("gethostbyname");
102 if(ping_target_host->h_addrtype!=AF_INET)
104 fprintf(stderr, "Got a hostent, but it doesn't have an IPv4 address");
107 ping_target = *(in_addr_t *)*ping_target_host->h_addr_list;
110 printf("Ping target is %s\n", inet_ntoa(*(struct in_addr *)*ping_target_host->h_addr_list));
113 /* Miscellaneous initialization */
115 sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
125 if(!no_daemon && daemon(1, 0)==-1)
128 openlog("netmon", 0, LOG_LOCAL7);
130 next_trigger = trigger_delay;
136 unsigned long long count;
138 gettimeofday(&tv, NULL);
140 /* Read network statistics */
141 snprintf(fnbuf, sizeof(fnbuf), "/sys/class/net/%s/statistics/rx_packets", interface);
142 count = read_number(fnbuf);
144 last_receive = tv.tv_sec;
147 snprintf(fnbuf, sizeof(fnbuf), "/sys/class/net/%s/statistics/tx_packets", interface);
148 count = read_number(fnbuf);
150 last_transmit = tv.tv_sec;
153 /* If packets have been transmitted more recently than received, there
154 might be a problem */
155 if(last_transmit>last_receive)
157 if(connection_status && tv.tv_sec>=last_receive+10)
159 connection_status = 0;
160 connection_down_time = last_receive;
162 printf("Connection is down\n");
164 syslog(LOG_INFO, "Connection is down");
167 if(trigger_cmd && tv.tv_sec>last_receive+next_trigger)
170 printf("Running %s\n", trigger_cmd);
172 syslog(LOG_INFO, "Running %s", trigger_cmd);
173 child_pid = run_command(trigger_cmd);
174 next_trigger += trigger_interval;
177 else if(!connection_status)
179 unsigned duration = tv.tv_sec-connection_down_time;
180 connection_status = 1;
182 printf("Connection is up (was down for %d seconds)\n", duration);
184 syslog(LOG_INFO, "Connection is up (was down for %d seconds)", duration);
186 next_trigger = trigger_delay;
191 /* Send ping packets to monitor packet loss */
192 if(tv.tv_sec>=next_ping)
198 printf("Lost ping\n");
201 send_ping(ping_target, seq);
206 next_ping = tv.tv_sec+1;
209 if(tv.tv_sec>=next_stats)
213 float loss_ratio = (float)lost/ping_count;
215 printf("Packet loss: %.2f%%\n", loss_ratio*100);
217 syslog(LOG_INFO, "Packet loss: %.2f%%", loss_ratio*100);
223 next_stats = tv.tv_sec+stats_interval;
227 /* Reap any finished child process */
230 if(waitpid(child_pid, NULL, WNOHANG)==child_pid)
234 res = poll(&pfd, 1, 1000);
237 struct sockaddr_in addr;
238 socklen_t alen = sizeof(addr);
242 struct icmphdr *icmp;
244 /* Receive a packet */
245 len = recvfrom(sock, data, sizeof(data), 0, (struct sockaddr *)&addr, &alen);
247 fprintf(stderr, "recvfrom error: %s\n", strerror(errno));
250 ip = (struct iphdr *)data;
251 if(ip->protocol==IPPROTO_ICMP)
253 icmp = (struct icmphdr *)(ip+1);
254 if(icmp->type==ICMP_ECHOREPLY && icmp->un.echo.id==pid)
256 /* It's an ICMP echo reply and ours, process it */
258 printf("Ping reply from %s\n", inet_ntoa(addr.sin_addr));
259 if(icmp->un.echo.sequence==pending)
262 printf("Sequence %d, expected %d\n", icmp->un.echo.sequence, pending);
274 unsigned long long read_number(const char *fn)
280 fd = open(fn, O_RDONLY);
284 len = read(fd, buf, sizeof(buf));
291 return strtoull(buf, NULL, 10);
294 unsigned checksum(const char *data, unsigned len)
299 for(i=0; i<len; i+=2)
300 sum += *(const unsigned short *)(data+i);
302 sum = (sum>>16)+(sum&0xFFFF);
307 void send_ping(in_addr_t target, uint16_t seq)
312 struct sockaddr_in addr;
314 for(i=0; i<sizeof(data); ++i)
317 hdr = (struct icmphdr *)data;
318 hdr->type = ICMP_ECHO;
321 hdr->un.echo.id = pid;
322 hdr->un.echo.sequence = seq;
323 hdr->checksum = checksum(data, sizeof(data));
325 addr.sin_family = AF_INET;
326 addr.sin_addr.s_addr = target;
329 if(sendto(sock, data, sizeof(data), 0, (struct sockaddr *)&addr, sizeof(addr))==-1)
330 fprintf(stderr, "sendto error: %s\n", strerror(errno));
333 pid_t run_command(const char *cmd)
343 execv(cmd, (char *const *)argv);