# Copyright (C) 2006-2011 OpenWrt.org
START=80
+USE_PROCD=1
-SERVICE_USE_PID=1
+. /lib/functions/network.sh
-start() {
- service_start /usr/sbin/apinger
+set_config_file() {
+ export CONFIG_FILE="/var/run/apinger-$instance.conf"
}
-stop() {
- service_stop /usr/sbin/apinger
+set_status_file() {
+ export STATUS_FILE="/var/run/apinger-$instance.status"
}
-reload() {
- service_reload /usr/sbin/apinger
+write_config_block() {
+ local cfg_var="CONFIG_BLOCK_$instance"
+ eval echo -e "\$$cfg_var" >> "$CONFIG_FILE"
+}
+
+start_config_block() {
+ eval "export CONFIG_BLOCK_$instance=''"
+ append CONFIG_BLOCK_$instance "$* {" "\n"
+}
+
+close_config_block() {
+ append CONFIG_BLOCK_$instance "}" "\n"
+}
+
+append_config_line() {
+ append CONFIG_BLOCK_$instance "\t$*" "\n"
+}
+
+append_target() {
+ local target=$1
+ local interface address probe_interval srcip
+ local avg_delay_samples avg_loss_samples avg_loss_delay_samples
+ local alarm_down alarm_delay alarm_loss alarms
+
+ config_get interface "$target" interface wan
+ [ "$interface" != "$instance" ] && return 0
+
+ config_get address "$target" address
+ config_get probe_interval "$target" probe_interval
+ config_get avg_delay_samples "$target" avg_delay_samples
+ config_get avg_loss_samples "$target" avg_loss_samples
+ config_get avg_loss_delay_samples "$target" avg_loss_delay_samples
+ config_get alarm_down "$target" alarm_down
+ config_get alarm_delay "$target" alarm_delay
+ config_get alarm_loss "$target" alarm_loss
+
+ [ -z "$address" ] && return 0
+
+ srcip=$(uci_get network "$interface" ipaddr)
+ [ -z "$srcip" ] && network_get_ipaddr srcip "$interface"
+ srcip="${srcip:-0.0.0.0}"
+
+ alarms=${alarm_down:+\"${alarm_down}\"}
+ alarms=${alarm_delay:+${alarms:+${alarms}, }}${alarm_delay:+\"${alarm_delay}\"}
+ alarms=${alarm_loss:+${alarms:+${alarms}, }}${alarm_loss:+\"${alarm_loss}\"}
+
+ start_config_block "target \"$address\""
+ append_config_line "srcip \"$srcip\""
+ append_config_line "description \"$target\""
+
+ [ -n "$probe_interval" ] && append_config_line "interval ${probe_interval}s"
+ [ -n "$avg_delay_samples" ] && append_config_line "avg_delay_samples ${avg_delay_samples}"
+ [ -n "$avg_loss_samples" ] && append_config_line "avg_loss_samples ${avg_loss_samples}"
+ [ -n "$avg_loss_delay_samples" ] && append_config_line "avg_loss_delay_samples ${avg_loss_delay_samples}"
+ [ -n "$alarms" ] && append_config_line "alarms override ${alarms}"
+
+ close_config_block
+ write_config_block
+}
+
+append_alarm_down() {
+ local alarm=$1
+ local time
+
+ config_get time "$alarm" time 5
+
+ [ -z "$time" ] && return
+
+ start_config_block "alarm down \"$alarm\""
+ append_config_line "time ${time}s"
+ close_config_block
+ write_config_block
+}
+
+append_alarm_delay() {
+ local alarm=$1
+ local delay_low delay_high
+
+ config_get delay_low "$alarm" delay_low
+ config_get delay_high "$alarm" delay_high
+
+ if [ -z "$delay_low" ] || [ -z "$delay_high" ]; then
+ return
+ fi
+
+ start_config_block "alarm delay \"$alarm\""
+ append_config_line "delay_low ${delay_low}ms"
+ append_config_line "delay_high ${delay_high}ms"
+ close_config_block
+ write_config_block
+}
+
+append_alarm_loss() {
+ local alarm=$1
+ local percent_low percent_high
+
+ config_get percent_low "$alarm" percent_low
+ config_get percent_high "$alarm" percent_low
+
+ if [ -z "$percent_low" ] || [ -z "$percent_high" ]; then
+ return
+ fi
+
+ start_config_block "alarm loss \"$alarm\""
+ append_config_line "percent_low ${percent_low}"
+ append_config_line "percent_high ${percent_high}"
+ close_config_block
+ write_config_block
+}
+
+init_apinger_config() {
+ local debug status_interval instance
+ instance=$1
+
+ config_get_bool debug apinger debug 0
+ config_get status_interval apinger status_interval 1
+
+ [ "$debug" == "1" ] && debug=on || debug=off
+
+ set_config_file
+ set_status_file
+
+ cat << EOF > "$CONFIG_FILE"
+user "root"
+group "root"
+debug ${debug}
+
+status {
+ scriptformat on
+ file "$STATUS_FILE"
+ interval ${status_interval}s
+}
+alarm down "down" {
+ time 30s
+}
+alarm delay "delay" {
+ delay_low 5ms
+ delay_high 20ms
+}
+alarm loss "loss" {
+ percent_low 3
+ percent_high 5
+}
+alarm default {
+ command on "/usr/libexec/apinger-hotplug up $instance '%t' '%i' '%T' '%a' '%A' '%r' '%p' '%P' '%l' '%d' '%s'"
+ command off "/usr/libexec/apinger-hotplug down $instance '%t' '%i' '%T' '%a' '%A' '%r' '%p' '%P' '%l' '%d' '%s'"
+}
+target default {
+ interval 1s
+ avg_delay_samples 10
+ avg_loss_samples 50
+ avg_loss_delay_samples 20
+ alarms "down", "delay", "loss"
+}
+EOF
+}
+
+start_instance() {
+ export instance=$1
+
+ local enabled
+ config_get_bool enabled "$instance" enabled 1
+ [ "$enabled" != "1" ] && return 0
+
+ init_apinger_config "$instance"
+ config_foreach append_alarm_down alarm_down
+ config_foreach append_alarm_delay alarm_delay
+ config_foreach append_alarm_loss alarm_loss
+ config_foreach append_target target
+
+ procd_open_instance "$instance"
+ procd_set_param command /usr/sbin/apinger -f -c $CONFIG_FILE
+ procd_set_param stderr 1
+ procd_set_param stdout 1
+ procd_close_instance
+}
+
+start_service() {
+ local instance=$1
+
+ config_load apinger
+
+ if [ -n "$instance" ]; then
+ start_instance "$instance"
+ else
+ config_foreach start_instance interface
+ fi
+}
+
+service_triggers() {
+ procd_add_reload_trigger apinger
+}
+
+clean_instance() {
+ local instance=$1
+
+ set_config_file
+ set_status_file
+
+ [ -e $CONFIG_FILE ] && rm -f $CONFIG_FILE
+ [ -e $STATUS_FILE ] && rm -f $STATUS_FILE
+}
+
+service_stopped() {
+ local instance=$1
+
+ config_load apinger
+
+ if [ -n "$instance" ]; then
+ clean_instance "$instance"
+ else
+ config_foreach clean_instance interface
+ fi
+}
+
+reload_service() {
+ restart
}
--- /dev/null
+--- a/src/apinger.c
++++ b/src/apinger.c
+@@ -161,6 +161,9 @@ time_t tim;
+ case 't':
+ values[n]=t->name;
+ break;
++ case 'i':
++ values[n]=t->config->srcip;
++ break;
+ case 'T':
+ values[n]=t->description;
+ break;
+@@ -276,6 +279,7 @@ time_t tm;
+ else
+ fprintf(f,"alarm canceled: %s\n",a->name);
+ fprintf(f,"Target: %s\n",t->name);
++ fprintf(f,"Source: %s\n",t->config->srcip);
+ fprintf(f,"Description: %s\n",t->description);
+ fprintf(f,"Probes sent: %i\n",t->last_sent+1);
+ fprintf(f,"Replies received: %i\n",t->received);
+@@ -645,7 +649,7 @@ void configure_targets(void){
+ struct target *t,*pt,*nt;
+ struct target_cfg *tc;
+ struct active_alarm_list *al,*nal;
+-union addr addr;
++union addr addr, srcaddr;
+ int r;
+ int l;
+
+@@ -665,6 +669,8 @@ int l;
+ nal=al->next;
+ free(al);
+ }
++ if (t->socket)
++ close(t->socket);
+ free(t->queue);
+ free(t->rbuf);
+ free(t->name);
+@@ -681,20 +687,16 @@ int l;
+ break;
+ if (t==NULL) { /* new target */
+ memset(&addr,0,sizeof(addr));
++ logit("Checking target IP %s", tc->srcip);
+ r=inet_pton(AF_INET,tc->name,&addr.addr4.sin_addr);
+ if (r){
+- if (icmp_sock<0){
+- logit("Sorry, IPv4 is not available\n");
+- logit("Ignoring target %s\n",tc->name);
+- continue;
+- }
+ addr.addr.sa_family=AF_INET;
+ }else{
+ #ifdef HAVE_IPV6
+ r=inet_pton(AF_INET6,tc->name,&addr.addr6.sin6_addr);
+ if (r==0){
+ #endif
+- logit("Bad host address: %s\n",tc->name);
++ logit("Bad target IP address: %s\n",tc->name);
+ logit("Ignoring target %s\n",tc->name);
+ continue;
+ #ifdef HAVE_IPV6
+@@ -707,12 +709,38 @@ int l;
+ addr.addr.sa_family=AF_INET6;
+ #endif
+ }
++ memset(&srcaddr,0,sizeof(srcaddr));
++ logit("Checking source IP %s", tc->srcip);
++ r=inet_pton(AF_INET,tc->srcip,&srcaddr.addr4.sin_addr);
++ if (r){
++ srcaddr.addr.sa_family=AF_INET;
++ }else{
++#ifdef HAVE_IPV6
++ r=inet_pton(AF_INET6,tc->srcip,&srcaddr.addr6.sin6_addr);
++ if (r==0){
++#endif
++ logit("Bad source IP address %s for target %s\n", tc->srcip, tc->name);
++ logit("Ignoring target %s\n",tc->name);
++ continue;
++#ifdef HAVE_IPV6
++ }
++ if (icmp6_sock<0){
++ logit("Sorry, IPv6 is not available\n");
++ logit("Ignoring target %s\n",tc->name);
++ continue;
++ }
++ srcaddr.addr.sa_family=AF_INET6;
++#endif
++ }
+ t=NEW(struct target,1);
+ memset(t,0,sizeof(struct target));
+ t->name=strdup(tc->name);
+ t->description=strdup(tc->description);
+ t->addr=addr;
++ t->ifaddr=srcaddr;
+ t->next=targets;
++ if(t->addr.addr.sa_family==AF_INET) make_icmp_socket(t);
++ if(t->addr.addr.sa_family==AF_INET6) make_icmp6_socket(t);
+ targets=t;
+ }
+ t->config=tc;
+@@ -752,6 +780,8 @@ struct active_alarm_list *al,*nal;
+ nal=al->next;
+ free(al);
+ }
++ if (t->socket)
++ close(t->socket);
+ free(t->queue);
+ free(t->rbuf);
+ free(t->name);
+@@ -799,6 +829,7 @@ char *buf1,*buf2;
+ fprintf(f,"%s\n",ctime(&tm));
+ for(t=targets;t;t=t->next){
+ fprintf(f,"Target: %s\n",t->name);
++ fprintf(f,"Source: %s\n",t->config->srcip);
+ fprintf(f,"Description: %s\n",t->description);
+ fprintf(f,"Last reply received: #%i %s",t->last_received,
+ ctime(&t->last_received_tv.tv_sec));
+@@ -909,7 +940,7 @@ int i;
+ void main_loop(void){
+ struct target *t;
+ struct timeval cur_time,next_status={0,0},tv,next_report={0,0},next_rrd_update={0,0};
+-struct pollfd pfd[2];
++struct pollfd pfd[1024];
+ int timeout;
+ int npfd=0;
+ int i;
+@@ -946,18 +977,8 @@ struct piped_info pi;
+ pfd[npfd].events=POLLIN|POLLERR|POLLHUP|POLLNVAL;
+ pfd[npfd].revents=0;
+ pfd[npfd++].fd=recv_pipe[0];
+-#else
+- if (icmp_sock){
+- pfd[npfd].events=POLLIN|POLLERR|POLLHUP|POLLNVAL;
+- pfd[npfd].revents=0;
+- pfd[npfd++].fd=icmp_sock;
+- }
+- if (icmp6_sock){
+- pfd[npfd].events=POLLIN|POLLERR|POLLHUP|POLLNVAL;
+- pfd[npfd++].fd=icmp6_sock;
+- pfd[npfd].revents=0;
+- }
+ #endif
++ memset(&pfd, '\0', sizeof pfd);
+ if (config->status_interval){
+ gettimeofday(&cur_time,NULL);
+ tv.tv_sec=config->status_interval/1000;
+@@ -965,10 +986,16 @@ struct piped_info pi;
+ timeradd(&cur_time,&tv,&next_status);
+ }
+ while(!interrupted_by){
++ npfd = 0;
+ gettimeofday(&cur_time,NULL);
+ if ( !timercmp(&next_probe,&cur_time,>) )
+ timerclear(&next_probe);
+ for(t=targets;t;t=t->next){
++ if (t->socket){
++ pfd[npfd].events=POLLIN|POLLERR|POLLHUP|POLLNVAL;
++ pfd[npfd].revents=0;
++ pfd[npfd++].fd=t->socket;
++ }
+ for(al=t->config->alarms;al;al=nal){
+ a=al->alarm;
+ nal=al->next;
+@@ -1051,8 +1078,20 @@ struct piped_info pi;
+ analyze_reply(pi.recv_timestamp,pi.icmp_seq,&pi.ti);
+ }
+ #else
+- if (pfd[i].fd==icmp_sock) recv_icmp();
+- else if (pfd[i].fd==icmp6_sock) recv_icmp6();
++ for(t=targets;t;t=t->next){
++ if (t->addr.addr.sa_family==AF_INET) {
++ if (t->socket == pfd[i].fd) {
++ recv_icmp(t);
++ break;
++ }
++ }
++ if (t->addr.addr.sa_family==AF_INET6) {
++ if (t->socket == pfd[i].fd) {
++ recv_icmp6(t);
++ break;
++ }
++ }
++ }
+ #endif
+ pfd[i].revents=0;
+ }
+--- a/src/apinger.conf
++++ b/src/apinger.conf
+@@ -47,6 +47,7 @@ alarm default {
+
+ ## Following "macros" may be used in options below:
+ ## %t - target name (address)
++ ## %i - source name (address)
+ ## %T - target description
+ ## %a - alarm name
+ ## %A - alarm type ("down"/"loss"/"delay")
+--- a/src/apinger.h
++++ b/src/apinger.h
+@@ -46,6 +46,8 @@
+ #endif
+ #include "conf.h"
+
++#include <ifaddrs.h>
++
+ union addr {
+ struct sockaddr addr;
+ struct sockaddr_in addr4;
+@@ -70,6 +72,7 @@ struct target {
+ char *queue; /*
+ contains info about recently sent packets
+ "1" means it was received */
++ int socket;
+ int last_sent; /* sequence number of the last ping sent */
+ int last_received; /* sequence number of the last ping received */
+ struct timeval last_received_tv; /* timestamp of the last ping received */
+@@ -89,6 +92,7 @@ struct target {
+ struct target_cfg *config;
+
+ struct target *next;
++ union addr ifaddr; /* iface address */
+ };
+
+ #define AVG_DELAY_KNOWN(t) (t->upsent >= t->config->avg_delay_samples)
+@@ -118,16 +122,16 @@ extern char *config_file;
+
+ extern int icmp_sock;
+ extern int icmp6_sock;
+-extern int ident;
++extern uint16_t ident;
+
+ extern struct timeval next_probe;
+
+-int make_icmp_socket(void);
+-void recv_icmp(void);
++int make_icmp_socket(struct target *t);
++void recv_icmp(struct target *t);
+ void send_icmp_probe(struct target *t,int seq);
+
+-int make_icmp6_socket(void);
+-void recv_icmp6(void);
++int make_icmp6_socket(struct target *t);
++void recv_icmp6(struct target *t);
+ void send_icmp6_probe(struct target *t,int seq);
+
+ #ifdef FORKED_RECEIVER
+--- a/src/cfgparser1.y
++++ b/src/cfgparser1.y
+@@ -96,6 +96,7 @@ struct target_cfg *cur_target;
+ %token DELAY_HIGH
+
+ %token DESCRIPTION
++%token SRCIP
+ %token ALARMS
+ %token INTERVAL
+ %token AVG_DELAY_SAMPLES
+@@ -247,6 +248,8 @@ target: TARGET getdeftarget DEFAULT '{'
+ targetcfg: /* */
+ | DESCRIPTION string
+ { cur_target->description=$2; }
++ | SRCIP string
++ { cur_target->srcip = $2; }
+ | ALARMS alarmlist
+ { cur_target->alarms=$2; }
+ | ALARMS OVERRIDE alarmlist
+--- a/src/cfgparser2.l
++++ b/src/cfgparser2.l
+@@ -81,6 +81,7 @@ delay { LOC; LOCINC; return DELAY; }
+ delay_high { LOC; LOCINC; return DELAY_HIGH; }
+ delay_low { LOC; LOCINC; return DELAY_LOW; }
+ description { LOC; LOCINC; return DESCRIPTION; }
++srcip { LOC; LOCINC; return SRCIP; }
+ down { LOC; LOCINC; return DOWN; }
+ false { LOC; LOCINC; return FALSE; }
+ file { LOC; LOCINC; return FILE_; }
+--- a/src/conf.c
++++ b/src/conf.c
+@@ -174,6 +174,14 @@ int ret;
+ }
+ }
+ for(t=cur_config.targets;t;t=t->next){
++ if (t->name==NULL || strlen(t->name)==0){
++ logit("Target name can't be empty.");
++ return 1;
++ }
++ else if (t->srcip==NULL){
++ logit("No source IP defined for target \"%s\".", t->name);
++ return 1;
++ }
+ if (t->description==NULL)
+ t->description=cur_config.target_defaults.description;
+ if (t->interval<=0)
+--- a/src/conf.h
++++ b/src/conf.h
+@@ -71,6 +71,7 @@ struct alarm_list {
+ struct target_cfg {
+ char *name;
+ char *description;
++ char *srcip;
+ int interval;
+ int avg_delay_samples;
+ int avg_loss_delay_samples;
+--- a/src/icmp6.c
++++ b/src/icmp6.c
+@@ -112,14 +112,14 @@ int ret;
+ memcpy(p+1,&ti,sizeof(ti));
+ size=sizeof(*p)+sizeof(ti);
+
+- ret=sendto(icmp6_sock,p,size,MSG_DONTWAIT,
++ ret=sendto(t->socket,p,size,MSG_DONTWAIT,
+ (struct sockaddr *)&t->addr.addr6,sizeof(t->addr.addr6));
+ if (ret<0){
+ if (config->debug) myperror("sendto");
+ }
+ }
+
+-void recv_icmp6(void){
++void recv_icmp6(struct target *t){
+ int len,icmplen,datalen;
+ char buf[1024];
+ char abuf[100];
+@@ -133,6 +133,7 @@ char ans_data[4096];
+ struct iovec iov;
+ struct msghdr msg;
+ struct cmsghdr *c;
++reloophack6:
+
+ iov.iov_base=buf;
+ iov.iov_len=1000;
+@@ -142,12 +143,13 @@ struct cmsghdr *c;
+ msg.msg_iovlen=1;
+ msg.msg_control=ans_data;
+ msg.msg_controllen=sizeof(ans_data);
+- len=recvmsg(icmp6_sock, &msg, MSG_DONTWAIT);
++ len=recvmsg(t->socket, &msg, MSG_DONTWAIT);
+ #else
+ socklen_t sl;
++reloophack6:
+
+ sl=sizeof(from);
+- len=recvfrom(icmp6_sock,buf,1024,0,(struct sockaddr *)&from,&sl);
++ len=recvfrom(t->socket,buf,1024,0,(struct sockaddr *)&from,&sl);
+ #endif
+ if (len<0){
+ if (errno==EAGAIN) return;
+@@ -169,7 +171,7 @@ socklen_t sl;
+ #endif
+ if (time_recvp==NULL){
+ #ifdef SIOCGSTAMP
+- if (!ioctl(icmp6_sock, SIOCGSTAMP, &time_recv)){
++ if (!ioctl(t->socket, SIOCGSTAMP, &time_recv)){
+ debug("Got timestamp from ioctl()");
+ }else
+ #endif
+@@ -182,8 +184,11 @@ socklen_t sl;
+ icmplen=len;
+ icmp=(struct icmp6_hdr *)buf;
+ if (icmp->icmp6_type != ICMP6_ECHO_REPLY) return;
+- if (icmp->icmp6_id != ident) return;
+-
++ if (icmp->icmp6_id != ident){
++ debug("Alien echo-reply received from xxx. Expected %i, received %i", ident, icmp->icmp6_id);
++ goto reloophack6;
++ return;
++ }
+ name=inet_ntop(AF_INET6,&from.sin6_addr,abuf,100);
+ debug("Ping reply from %s",name);
+ datalen=icmplen-sizeof(*icmp);
+@@ -199,33 +204,36 @@ socklen_t sl;
+ }
+
+
+-int make_icmp6_socket(void){
++int make_icmp6_socket(struct target *t){
+ int opt;
+
+- icmp6_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+- if (icmp6_sock<0)
++ t->socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
++ if (t->socket <0)
+ myperror("socket");
+ else {
+ opt=2;
+ #if defined(SOL_RAW) && defined(IPV6_CHECKSUM)
+- if (setsockopt(icmp6_sock, SOL_RAW, IPV6_CHECKSUM, &opt, sizeof(int)))
++ if (setsockopt(t->socket, SOL_RAW, IPV6_CHECKSUM, &opt, sizeof(int)))
+ myperror("setsockopt(IPV6_CHECKSUM)");
+ #endif
+ #ifdef SO_TIMESTAMP
+ opt=1;
+- if (setsockopt(icmp6_sock, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)))
++ if (setsockopt(t->socket, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)))
+ myperror("setsockopt(SO_TIMESTAMP)");
+ #endif
+ /*install_filter6();*/
+ }
+- return icmp6_sock;
++ if (bind(t->socket, (struct sockaddr *)&t->ifaddr.addr6, sizeof(t->ifaddr.addr6)) < 0)
++ myperror("bind socket");
++
++ return t->socket;
+ }
+
+ #else /*HAVE_IPV6*/
+ #include "apinger.h"
+
+-int make_icmp6_socket(void){ return -1; }
+-void recv_icmp6(void){}
++int make_icmp6_socket(struct target *t){ return -1; }
++void recv_icmp6(struct target *t){}
+ void send_icmp6_probe(struct target *t,int seq){}
+
+ #endif /*HAVE_IPV6*/
+--- a/src/icmp.c
++++ b/src/icmp.c
+@@ -150,14 +150,14 @@ int ret;
+ size=sizeof(*p)+sizeof(ti);
+
+ p->icmp_cksum = in_cksum((u_short *)p,size,0);
+- ret=sendto(icmp_sock,p,size,MSG_DONTWAIT,
++ ret=sendto(t->socket,p,size,MSG_DONTWAIT,
+ (struct sockaddr *)&t->addr.addr4,sizeof(t->addr.addr4));
+ if (ret<0){
+ if (config->debug) myperror("sendto");
+ }
+ }
+
+-void recv_icmp(void){
++void recv_icmp(struct target *t){
+ int len,hlen,icmplen,datalen;
+ char buf[1024];
+ struct sockaddr_in from;
+@@ -170,6 +170,7 @@ char ans_data[4096];
+ struct iovec iov;
+ struct msghdr msg;
+ struct cmsghdr *c;
++reloophack:
+
+ iov.iov_base=buf;
+ iov.iov_len=1000;
+@@ -179,12 +180,13 @@ struct cmsghdr *c;
+ msg.msg_iovlen=1;
+ msg.msg_control=ans_data;
+ msg.msg_controllen=sizeof(ans_data);
+- len=recvmsg(icmp_sock, &msg, MSG_DONTWAIT);
++ len=recvmsg(t->socket, &msg, MSG_DONTWAIT);
+ #else
+ socklen_t sl;
++reloophack:
+
+ sl=sizeof(from);
+- len=recvfrom(icmp_sock,buf,1024,MSG_DONTWAIT,(struct sockaddr *)&from,&sl);
++ len=recvfrom(t->socket,buf,1024,MSG_DONTWAIT,(struct sockaddr *)&from,&sl);
+ #endif
+ if (len<0){
+ if (errno==EAGAIN) return;
+@@ -196,7 +198,7 @@ socklen_t sl;
+ debug("checking CMSG...");
+ for (c = CMSG_FIRSTHDR(&msg); c; c = CMSG_NXTHDR(&msg, c)) {
+ debug("CMSG level: %i type: %i",c->cmsg_level,c->cmsg_type);
+- if (c->cmsg_level != SOL_SOCKET || c->cmsg_type != SO_TIMESTAMP)
++ if (c->cmsg_level != SOL_SOCKET || c->cmsg_type != SCM_TIMESTAMP)
+ continue;
+ if (c->cmsg_len < CMSG_LEN(sizeof(struct timeval)))
+ continue;
+@@ -206,7 +208,7 @@ socklen_t sl;
+ #endif
+ if (time_recvp==NULL){
+ #ifdef SIOCGSTAMP
+- if (!ioctl(icmp_sock, SIOCGSTAMP, &time_recv)){
++ if (!ioctl(t->socket, SIOCGSTAMP, &time_recv)){
+ debug("Got timestampt from ioctl()");
+ }else
+ #endif
+@@ -226,7 +228,8 @@ socklen_t sl;
+ return;
+ }
+ if (icmp->icmp_id != ident){
+- debug("Alien echo-reply received");
++ debug("Alien echo-reply received from %s. Expected %i, received %i",inet_ntoa(from.sin_addr), ident, icmp->icmp_id);
++ goto reloophack;
+ return;
+ }
+ debug("Ping reply from %s",inet_ntoa(from.sin_addr));
+@@ -242,19 +245,23 @@ socklen_t sl;
+ #endif
+ }
+
+-int make_icmp_socket(void){
++int make_icmp_socket(struct target *t){
+ int on;
+
+- icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+- if (icmp_sock<0)
++ t->socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
++ if (t->socket < 0)
+ myperror("socket");
+ #ifdef SO_TIMESTAMP
+ else{
+ on=1;
+- if (setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)))
++ if (setsockopt(t->socket, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)))
+ myperror("setsockopt(SO_TIMESTAMP)");
+ }
+ #endif
+- return icmp_sock;
++
++ if (bind(t->socket, (struct sockaddr *)&t->ifaddr.addr4, sizeof(t->ifaddr.addr4)) < 0)
++ myperror("bind socket");
++
++ return t->socket;
+ }
+
+--- a/src/main.c
++++ b/src/main.c
+@@ -71,6 +71,7 @@ struct config default_config={
+ { /* target defaults */
+ "default", /* name */
+ "", /* description */
++ "", /* interface */
+ 1000, /* interval */
+ 20, /* avg_delay_samples */
+ 5, /* avg_loss_delay_samples */
+@@ -95,7 +96,7 @@ char *config_file=CONFIG;
+
+ int icmp_sock;
+ int icmp6_sock;
+-int ident;
++uint16_t ident;
+
+ struct timeval next_probe={0,0};
+
+@@ -216,12 +217,6 @@ char *graph_location="/apinger/";
+ }
+ }
+
+- make_icmp_socket();
+- make_icmp6_socket();
+- if (icmp6_sock<0 && icmp_sock<0){
+- return 1;
+- }
+-
+ pw=getpwnam(config->user);
+ if (!pw) {
+ debug("getpwnam(\"%s\") failed.",config->user);
+@@ -276,7 +271,7 @@ char *graph_location="/apinger/";
+ return 1;
+ }
+
+- ident=getpid();
++ ident=getpid() & 0xFFFF;
+ signal(SIGTERM,signal_handler);
+ signal(SIGINT,signal_handler);
+ signal(SIGHUP,signal_handler);
+@@ -285,9 +280,8 @@ char *graph_location="/apinger/";
+ #ifdef FORKED_RECEIVER
+ signal(SIGCHLD,sigchld_handler);
+ #endif
++ logit("Starting Alarm Pinger, apinger(%i)", ident);
+ main_loop();
+- if (icmp_sock>=0) close(icmp_sock);
+- if (icmp6_sock>=0) close(icmp6_sock);
+
+ logit("Exiting on signal %i.",interrupted_by);
+
--- /dev/null
+--- a/src/apinger.c
++++ b/src/apinger.c
+@@ -826,26 +826,49 @@ char *buf1,*buf2;
+ return;
+ }
+ tm=time(NULL);
+- fprintf(f,"%s\n",ctime(&tm));
++ if(!config->status_format) fprintf(f,"%s\n",ctime(&tm));
+ for(t=targets;t;t=t->next){
+- fprintf(f,"Target: %s\n",t->name);
+- fprintf(f,"Source: %s\n",t->config->srcip);
+- fprintf(f,"Description: %s\n",t->description);
+- fprintf(f,"Last reply received: #%i %s",t->last_received,
+- ctime(&t->last_received_tv.tv_sec));
+- fprintf(f,"Average delay: %0.3fms\n",AVG_DELAY(t));
++ if(config->status_format){
++ fprintf(f,"%s|%s|%s|%i|%i|%u|",t->name, t->config->srcip, t->description, t->last_sent+1,
++ t->received, t->last_received_tv.tv_sec);
++ fprintf(f,"%0.3fms|", AVG_DELAY(t));
++ }
++ else{
++ fprintf(f,"Target: %s\n",t->name);
++ fprintf(f,"Source: %s\n",t->config->srcip);
++ fprintf(f,"Description: %s\n",t->description);
++ fprintf(f,"Last reply received: #%i %s",t->last_received,
++ ctime(&t->last_received_tv.tv_sec));
++ fprintf(f,"Average delay: %0.3fms\n",AVG_DELAY(t));
++ }
+ if (AVG_LOSS_KNOWN(t)){
+- fprintf(f,"Average packet loss: %0.1f%%\n",AVG_LOSS(t));
++ if(config->status_format){
++ fprintf(f,"%0.1f%%",AVG_LOSS(t));
++ }
++ else{
++ fprintf(f,"Average packet loss: %0.1f%%\n",AVG_LOSS(t));
++ }
++ }
++ if(config->status_format){
++ fprintf(f, "|");
++ }
++ else{
++ fprintf(f,"Active alarms: ");
+ }
+- fprintf(f,"Active alarms:");
+ if (t->active_alarms){
+ for(al=t->active_alarms;al;al=al->next){
+ a=al->alarm;
+- fprintf(f," \"%s\"",a->name);
++ if(config->status_format){
++ fprintf(f,"%s",a->name);
++ }
++ else{
++ fprintf(f," \"%s\"",a->name);
++ }
+ }
+- fprintf(f,"\n");
++ if(!config->status_format) fprintf(f,"\n");
+ }
+- else fprintf(f," None\n");
++ else fprintf(f,"none");
++ if(!config->status_format) fprintf(f,"\n");
+
+ buf1=NEW(char,t->config->avg_loss_delay_samples+1);
+ buf2=NEW(char,t->config->avg_loss_samples+1);
+--- a/src/apinger.conf
++++ b/src/apinger.conf
+@@ -29,6 +29,10 @@ group "nogroup"
+ # ## Interval between file updates
+ # ## when 0 or not set, file is written only when SIGUSR1 is received
+ # interval 5m
++#
++# ## Create status file in script parseable format (on) or human
++# ## readable format (off)
++# scriptformat off
+ #}
+
+ ########################################
+--- a/src/cfgparser1.y
++++ b/src/cfgparser1.y
+@@ -104,6 +104,7 @@ struct target_cfg *cur_target;
+ %token AVG_LOSS_DELAY_SAMPLES
+
+ %token FILE_
++%token SCRIPTFORMAT
+
+ %token ERROR
+
+@@ -282,6 +283,8 @@ statuscfg: /* */
+ { cur_config.status_interval=$2; }
+ | INTERVAL TIME
+ { cur_config.status_interval=$2; }
++ | SCRIPTFORMAT boolean
++ { cur_config.status_format=$2; }
+ | statuscfg separator statuscfg
+ ;
+
+--- a/src/cfgparser2.l
++++ b/src/cfgparser2.l
+@@ -85,6 +85,7 @@ srcip { LOC; LOCINC; return SRCIP; }
+ down { LOC; LOCINC; return DOWN; }
+ false { LOC; LOCINC; return FALSE; }
+ file { LOC; LOCINC; return FILE_; }
++scriptformat { LOC; LOCINC; return SCRIPTFORMAT; }
+ group { LOC; LOCINC; return GROUP; }
+ interval { LOC; LOCINC; return INTERVAL; }
+ loss { LOC; LOCINC; return LOSS; }
+--- a/src/conf.h
++++ b/src/conf.h
+@@ -97,6 +97,7 @@ struct config {
+ char *pid_file;
+ char *status_file;
+ int status_interval;
++ int status_format;
+ char *timestamp_format;
+ };
+
+--- a/src/main.c
++++ b/src/main.c
+@@ -88,6 +88,7 @@ struct config default_config={
+ "/var/run/apinger.pid", /* pid file */
+ NULL, /* status file */
+ 0, /* status interval */
++ 0, /* status format */
+ "%b %d %H:%M:%S" /* timestamp format */
+ };
+