I am writing a ping program using raw sockets but recvfrom is not returning -1 with EINTR even though a SIGALRM is being handled.This SIGALRM is produced by my alarm(1).I want recvfrom to return so that i can decide that the packet has indeed been lost.
#include "libsock"
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
double total=0, max=0, min=10000000;
int totalpackets=0, packetslost=0;
int recieved=0;
void handler2()
{
printf("host unreachable\n");
}
unsigned short
csum (unsigned short *buf, int nwords)
{
unsigned long sum;
for (sum = 0; nwords > 0; nwords--)
sum += *buf++;
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return ~sum;
}
void
handler()
{
printf("\n");
printf("--------------PINGING STATISTICS----------------\n");
printf("AVG:%f MAX:%f MIN:%f TOTAL PACKETS:%d PACKETS LOST:%d SUCCESS PERCENTAGE:%f\n\n",total/(totalpackets-packetslost),max,min,totalpackets,packetslost,((double)(totalpackets-packetslost)/(double)totalpackets)*100);
exit(0);
}
int
main (int argc, char *argv[])
{
if (argc != 2)
{
printf ("need destination for tracert\n");
exit (0);
}
int sfd = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
char buf[4096] = { 0 };
int one = 1;
const int *val = &one;
if (setsockopt (sfd, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0)
printf ("Cannot set HDRINCL!\n");
struct sockaddr_in addr;
struct ip* ip_hdr=(struct ip*)buf;
addr.sin_port = htons (7);
addr.sin_family = AF_INET;
inet_pton (AF_INET, argv[1], &(addr.sin_addr));
ip_hdr->ip_hl = 5;
ip_hdr->ip_v = 4;
ip_hdr->ip_tos = 0;
ip_hdr->ip_len = 20 + 8 + 64;
ip_hdr->ip_id =0;
ip_hdr->ip_off = 64;
ip_hdr->ip_ttl = 64;
ip_hdr->ip_p = IPPROTO_ICMP;
inet_pton (AF_INET, "172.30.104.59", &(ip_hdr->ip_src));
inet_pton (AF_INET, argv[1], &(ip_hdr->ip_dst));
ip_hdr->ip_sum = csum ((unsigned short *) buf, 4);
struct icmphdr *icmphd = (struct icmphdr *) (buf+20);
icmphd->type = ICMP_ECHO;
icmphd->code = 0;
icmphd->checksum = 0;
icmphd->un.echo.id = 0;
icmphd->un.echo.sequence =1;
memset(buf+28,'a',64);
icmphd->checksum = csum ((unsigned short *) (buf+20), 36);
signal(SIGINT,handler);
struct timeval tv1,tv2;
printf("Pinging %s with 64 bytes of data.\n\n",argv[1]);
while(1)
{
recieved=0;
totalpackets++;
sendto (sfd, buf,20+ 8+64, 0, SA & addr, sizeof addr);
char buff[4096] = { 0 };
struct sockaddr_in addr2;
socklen_t len = sizeof (struct sockaddr_in);
signal(SIGALRM,handler2);
gettimeofday(&tv1,NULL);
alarm(1);
int x=recvfrom (sfd, buff, 20+8+64, 0, SA & addr2, &len);
gettimeofday(&tv2,NULL);
double d=(double)(tv2.tv_sec-tv1.tv_sec)*1000+((double)(tv2.tv_usec-tv1.tv_usec))/1000;
if(x>0) {
struct icmphdr *icmphd2 = (struct icmphdr *) (buff + 20);
printf ("Reached destination:%s\tTime elapsed:%f ms\n\n",
inet_ntoa (addr2.sin_addr),d);
total+=d;
if(d>max)
max=d;
if(d<min)
min=d;
} else {
printf("Packet lost\n");
packetslost++;
}
sleep(1);
}
return 0;
}
libsock contains headers and SA=(struct sockaddr*)
Is SIGALRM different from other signals, I have not set SA_RESTART..
Thanks.
signal
's exact behaviour is system-dependent. From man 2 signal
:
The BSD semantics are equivalent to calling
sigaction(2)
with the following flags:sa.sa_flags = SA_RESTART;
The situation on Linux is as follows:
...
By default, in glibc 2 and later, the
signal()
wrapper function does not invoke the kernel system call. Instead, it calls sigaction(2) using flags that supply BSD semantics.
(emphasis mine)
Thus, by default, on Linux and BSD systems, signal
will set SA_RESTART
, which will automatically restart your system call so that it never reports EINTR
.
You need to use sigaction
to get precise control over the flags:
struct sigaction sact = {
.sa_handler = handle_sigalrm,
.sa_flags = 0,
};
sigaction(SIGALRM, &sact, NULL);