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>
22 #include <pcap/pcap.h>
23 #include <netinet/ether.h>
24 #include <netinet/ip6.h>
26 typedef unsigned long long Time;
30 struct sockaddr_storage address;
31 struct sockaddr_storage mask;
42 int connection_status;
43 Time connection_down_time;
48 const char *target_name;
49 struct sockaddr_in target_addr;
61 int monitor_init(Monitor *);
62 void monitor_check(Monitor *, Time);
63 void capture_handler(uint8_t *, const struct pcap_pkthdr *, const uint8_t *);
64 int get_inet_scope(uint32_t, const Address *);
65 int get_inet6_scope(const struct in6_addr *, const Address *);
66 int pinger_init(Pinger *);
67 void pinger_check(Pinger *, Time);
68 unsigned checksum(const char *, unsigned);
69 void send_ping(Pinger *);
70 pid_t run_command(const char *);
75 int main(int argc, char **argv)
81 Time stats_interval = 1800000000ULL;
82 Time trigger_delay = 60000000ULL;
83 Time trigger_interval = 600000000ULL;
84 const char *trigger_cmd = NULL;
88 monitor.interface_name = NULL;
89 pinger.target_name = NULL;
92 while((o = getopt(argc, argv, "fvs:t:c:i:p:"))!=-1)
102 stats_interval = strtoul(optarg, NULL, 10)*1000000;
105 trigger_delay = strtoul(optarg, &endp, 10)*1000000;
107 trigger_interval = strtoul(endp+1, NULL, 10)*1000000;
109 trigger_interval = trigger_delay;
112 trigger_cmd = optarg;
115 monitor.interface_name = strdup(optarg);
118 pinger.target_name = strdup(optarg);
122 if(monitor.interface_name)
123 monitor_init(&monitor);
125 if(pinger.target_name)
126 pinger_init(&pinger);
128 if(!no_daemon && daemon(1, 0)==-1)
131 openlog("netmon", 0, LOG_LOCAL7);
133 Time next_stats = current_time()+stats_interval;
134 Time next_trigger = trigger_delay;
138 Time time = current_time();
140 if(monitor.interface_name)
142 monitor_check(&monitor, time);
144 if(monitor.connection_status)
145 next_trigger = trigger_delay;
146 else if(time>=monitor.last_receive+next_trigger)
149 printf("Running %s\n", trigger_cmd);
151 syslog(LOG_INFO, "Running %s", trigger_cmd);
152 child_pid = run_command(trigger_cmd);
153 next_trigger += trigger_interval;
157 if(pinger.target_name)
159 pinger_check(&pinger, time);
165 float loss_ratio = (float)pinger.lost/pinger.count;
167 printf("Packet loss: %.2f%%\n", loss_ratio*100);
169 syslog(LOG_INFO, "Packet loss: %.2f%%", loss_ratio*100);
174 next_stats += stats_interval;
178 /* Reap any finished child process */
181 if(waitpid(child_pid, NULL, WNOHANG)==child_pid)
194 gettimeofday(&tv, NULL);
195 return tv.tv_sec*1000000ULL+tv.tv_usec;
198 int monitor_init(Monitor *monitor)
200 char err[PCAP_ERRBUF_SIZE];
202 if(pcap_findalldevs(&devs, err)==-1)
204 fprintf(stderr, "pcap_findalldevs: %s\n", err);
208 monitor->pcap = pcap_open_live(monitor->interface_name, 64, 0, 10, err);
211 fprintf(stderr, "pcap_findalldevs: %s\n", err);
215 if(pcap_setnonblock(monitor->pcap, 1, err)==-1)
217 fprintf(stderr, "pcap_findalldevs: %s\n", err);
221 monitor->n_addresses = 0;
222 monitor->addresses = NULL;
223 for(pcap_if_t *d=devs; d; d=d->next)
224 for(pcap_addr_t *a=d->addresses; a; a=a->next)
225 if(a->addr->sa_family==AF_INET || a->addr->sa_family==AF_INET6)
227 monitor->addresses = (Address *)realloc(monitor->addresses, (monitor->n_addresses+1)*sizeof(Address));
228 unsigned size = (a->addr->sa_family==AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
229 memcpy(&monitor->addresses[monitor->n_addresses].address, a->addr, size);
230 memcpy(&monitor->addresses[monitor->n_addresses].mask, a->netmask, size);
231 ++monitor->n_addresses;
234 pcap_freealldevs(devs);
236 monitor->last_receive = 0;
237 monitor->last_transmit = 0;
238 monitor->connection_status = 1;
243 void monitor_check(Monitor *monitor, Time timestamp)
245 pcap_dispatch(monitor->pcap, -1, &capture_handler, (uint8_t *)monitor);
247 if(monitor->last_transmit && monitor->last_receive)
249 /* If packets have been transmitted more recently than received, there
250 might be a problem */
251 if(monitor->last_transmit>monitor->last_receive)
253 if(monitor->connection_status && timestamp>=monitor->last_receive+10000000)
255 monitor->connection_status = 0;
256 monitor->connection_down_time = monitor->last_receive;
258 printf("Connection is down\n");
260 syslog(LOG_INFO, "Connection is down");
263 else if(!monitor->connection_status)
265 Time duration = timestamp-monitor->connection_down_time;
266 monitor->connection_status = 1;
268 printf("Connection is up (was down for %lld seconds)\n", duration/1000000);
270 syslog(LOG_INFO, "Connection is up (was down for %lld seconds)", duration/1000000);
275 void capture_handler(uint8_t *user, const struct pcap_pkthdr *header, const uint8_t *data)
277 Monitor *monitor = (Monitor *)user;
278 const struct ethhdr *eth = (const struct ethhdr *)(data);
282 int proto = ntohs(eth->h_proto);
285 const struct iphdr *ip = (const struct iphdr *)(eth+1);
286 if(ntohl(ip->daddr)>>28==14)
289 for(unsigned i=0; i<monitor->n_addresses; ++i)
290 if(monitor->addresses[i].address.ss_family==AF_INET)
292 int s = get_inet_scope(ip->saddr, &monitor->addresses[i]);
296 s = get_inet_scope(ip->daddr, &monitor->addresses[i]);
301 else if(proto==ETH_P_IPV6)
303 const struct ip6_hdr *ip6 = (const struct ip6_hdr *)(eth+1);
304 for(unsigned i=0; i<monitor->n_addresses; ++i)
305 if(monitor->addresses[i].address.ss_family==AF_INET6)
307 int s = get_inet6_scope(&ip6->ip6_src, &monitor->addresses[i]);
311 s = get_inet6_scope(&ip6->ip6_dst, &monitor->addresses[i]);
317 Time timestamp = header->ts.tv_sec*1000000ULL+header->ts.tv_usec;
318 if(src_scope==0 && dst_scope>0)
319 monitor->last_receive = timestamp;
320 else if(src_scope>0 && dst_scope==0)
321 monitor->last_transmit = timestamp;
324 int get_inet_scope(uint32_t addr, const Address *local_addr)
326 uint32_t diff = addr^((struct sockaddr_in *)&local_addr->address)->sin_addr.s_addr;
329 else if(!(diff&((struct sockaddr_in *)&local_addr->mask)->sin_addr.s_addr))
335 int get_inet6_scope(const struct in6_addr *addr, const Address *local_addr)
338 for(unsigned i=0; i<16; ++i)
340 uint8_t diff = addr->s6_addr[i]^((struct sockaddr_in6 *)&local_addr->address)->sin6_addr.s6_addr[i];
343 diff &= ((struct sockaddr_in6 *)&local_addr->mask)->sin6_addr.s6_addr[i];
354 int pinger_init(Pinger *pinger)
356 if(!pinger->target_name)
359 struct hostent *host;
361 host = gethostbyname(pinger->target_name);
364 herror("gethostbyname");
368 if(host->h_addrtype!=AF_INET)
370 fprintf(stderr, "Got a hostent, but it doesn't have an IPv4 address");
374 pinger->target_addr.sin_family = AF_INET;
375 pinger->target_addr.sin_addr = *(struct in_addr *)host->h_addr_list[0];
380 inet_ntop(AF_INET, &pinger->target_addr.sin_addr, buf, sizeof(buf));
381 printf("Ping target is %s\n", buf);
384 pinger->socket = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
385 if(pinger->socket==-1)
392 pinger->id = getpid();
398 pinger->pfd.fd = pinger->socket;
399 pinger->pfd.events = POLLIN;
404 void pinger_check(Pinger *pinger, Time time)
406 if(poll(&pinger->pfd, 1, 10)>0)
408 struct sockaddr_in addr;
409 socklen_t alen = sizeof(addr);
411 /* Receive a packet */
413 int len = recvfrom(pinger->socket, data, sizeof(data), 0, (struct sockaddr *)&addr, &alen);
415 fprintf(stderr, "recvfrom error: %s\n", strerror(errno));
418 struct iphdr *ip = (struct iphdr *)data;
419 if(ip->protocol==IPPROTO_ICMP)
421 struct icmphdr *icmp = (struct icmphdr *)(ip+1);
422 if(icmp->type==ICMP_ECHOREPLY && icmp->un.echo.id==pinger->id)
424 /* It's an ICMP echo reply and ours, process it */
428 inet_ntop(AF_INET, &addr.sin_addr, buf, sizeof(buf));
429 printf("Ping reply from %s\n", buf);
432 if(icmp->un.echo.sequence==pinger->pending)
435 printf("Sequence %d, expected %d\n", icmp->un.echo.sequence, pinger->pending);
441 if(time>=pinger->next)
447 printf("Lost ping\n");
451 pinger->next = time+1000000;
455 unsigned checksum(const char *data, unsigned len)
458 for(unsigned i=0; i<len; i+=2)
459 sum += *(const unsigned short *)(data+i);
461 sum = (sum>>16)+(sum&0xFFFF);
466 void send_ping(Pinger *pinger)
469 for(unsigned i=0; i<sizeof(data); ++i)
472 struct icmphdr *hdr = (struct icmphdr *)data;
473 hdr->type = ICMP_ECHO;
476 hdr->un.echo.id = pinger->id;
477 hdr->un.echo.sequence = pinger->seq;
478 hdr->checksum = checksum(data, sizeof(data));
480 if(sendto(pinger->socket, data, sizeof(data), 0, (struct sockaddr *)&pinger->target_addr, sizeof(struct sockaddr_in))==-1)
481 fprintf(stderr, "sendto error: %s\n", strerror(errno));
483 pinger->pending = pinger->seq++;
489 pid_t run_command(const char *cmd)
499 execv(cmd, (char *const *)argv);