Reputation: 251
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.
Upvotes: 4
Views: 1693
Reputation: 6228
You should not use signal
because it is non portable. Instead, use sigaction
.
With the option SA_RESTART
, the blocking call will be silently resumed after the signal is caught.
Without SA_RESTART
, the blocking call stops, returns -1 and set errno
to EINTR
.
Here is a function mysignal
, which you can use to replace your signal
call with options
set to 0:
int mysignal (int sig, void (*func)(int), int options) {
int r;
struct sigaction act;
act.sa_handler = func;
act.sa_flags = options;
sigemptyset (&act.sa_mask);
r = sigaction (sig, &act, NULL);
if (r < 0) perror (__func__);
return r;
}
Upvotes: 1
Reputation: 179412
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);
Upvotes: 3
Reputation: 488133
The behavior of using "plain" signal
is to a large extent up to the OS. Assuming POSIX conformance, you should use sigaction
instead. See What is the difference between sigaction and signal? for more.
Upvotes: 2