Recvfrom does not return -1 after the signal

I am writing a ping program using raw sockets, but recvfrom does not return -1 with EINTR, even if SIGALRM is being processed. This SIGALRM is created by my alarm (1). I want recvfrom to return so that I can decide that the packet is really 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 did not set SA_RESTART ..

Thanks.

+4
source share
3 answers

signal exact behavior is system dependent. From man 2 signal :

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 wrapper signal() function does not call the kernel system call. Instead, it calls sigaction (2), using flags that provide BSD semantics.

(my emphasis)

Thus, by default on Linux and BSD signal SA_RESTART , SA_RESTART will be set, which will automatically restart your system call so that it never 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); 
+3
source

The behavior of using the "plain" signal is highly OS dependent. Assuming POSIX compliance, you should use sigaction . See What is the difference between sigaction and signal? for more details.

+2
source

You cannot use signal because it is not portable. Use sigaction .

With the SA_RESTART option SA_RESTART blocking call will be silently resumed after the signal is caught. Without SA_RESTART blocking call stops, returns -1, and sets errno to EINTR .

Here is the mysignal function that 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; } 
+1
source

All Articles