// simple ping #include #include #include #include #include #include #include #include #include typedef unsigned char byte; #define warn(a...) do{fprintf(stderr, a); }while(0) #define die(a...) do{fprintf(stderr, a); exit(1);}while(0) #define diesys(a...) do{fprintf(stderr, a); perror(" sys"); exit(1);}while(0) static struct{ unsigned seqno; uint64_t cur_us; struct sockaddr_in pingaddr; int myid; }ping; static uint16_t inet_cksum(void const* ptr, unsigned sz){ byte const* p = ptr; uint32_t sum = 0; uint16_t u16; for(;sz>=2; sz-=2,p+=2){ memcpy(&u16, p, sizeof(u16)); sum += u16; } if(1==sz){ memcpy(&u16, (byte[]){*p, 0}, sizeof(u16)); sum += u16; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return (uint16_t)~sum; } static int create_icmp_sock(void){ enum{icmp_protocol=1}; int sock = socket(AF_INET, SOCK_RAW, icmp_protocol); if(sock<1) diesys("socket"); int sockopt=1; if(setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &sockopt, sizeof(sockopt))) die("setsockopt SO_BROADCAST"); return sock; } static uint64_t nowus(void){ struct timespec ts; if(clock_gettime(CLOCK_MONOTONIC, &ts)) diesys("clock_gettime"); uint64_t const us = ts.tv_sec * 1000000ULL + ts.tv_nsec/1000; return us; } static void show(struct sockaddr_in* from, unsigned sz, struct icmp *icmp){ assert(sz>=sizeof(struct icmp)); static char ipaddrstr[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(from->sin_addr), ipaddrstr, INET_ADDRSTRLEN); fprintf(stderr, "%u bytes from from (%s)", sz, ipaddrstr); uint64_t tus; memcpy(&tus, icmp->icmp_data, sizeof(tus)); fprintf(stderr, " elapsed time %0.3fus\n", (double)(nowus()-tus)/1000); } static void send_ping(int sock){ enum{noflags=0}; struct icmp icmp; memset(&icmp, 0xab, sizeof(icmp)); icmp.icmp_type = ICMP_ECHO; icmp.icmp_code = 0; icmp.icmp_cksum = 0; // must=zero for checksum calc icmp.icmp_seq = htons(ping.seqno); icmp.icmp_id = ping.myid = getpid(); ++ping.seqno; uint64_t tus = nowus(); memcpy(icmp.icmp_data, &tus, sizeof(tus)); // the icmp_dun{} data union is big enough for the timestamp size_t const size_pkt = sizeof(struct icmp); icmp.icmp_cksum = inet_cksum(&icmp, size_pkt); ssize_t sz = sendto(sock, &icmp, size_pkt, noflags, (struct sockaddr*)&ping.pingaddr, sizeof(ping.pingaddr)); if(size_pkt != sz) diesys("sendto"); } static void await_pingback(int sock){ enum{noflags=0}; struct sockaddr_in from; socklen_t fromlen = (socklen_t)sizeof(from); byte pkt[sizeof(struct iphdr) + sizeof(struct icmp)]; for(;;){ int const sz = recvfrom(sock, pkt, sizeof(pkt), noflags, (struct sockaddr *)&from, &fromlen); if(sz<0){ if(EINTR==errno) continue; diesys("recvfrom"); } if(sz < (int)(sizeof(struct icmp))){ warn("received packet is too small size:%d\n", sz); continue; } struct iphdr *iphdr = (struct iphdr *)pkt; struct icmp *icmp = (struct icmp *)(pkt + (iphdr->ihl * 4)); if(icmp->icmp_id != ping.myid){ warn("not our ping id:%d\n", icmp->icmp_id); continue; } switch(icmp->icmp_type){ case ICMP_ECHOREPLY: show(&from, sz, icmp); return; break; case ICMP_ECHO: /*ignore, echo from us*/; break; default: warn("received not ICMP_ECHOREPLY/ICMP_ECHO type:icmp->icmp_type:%d\n", icmp->icmp_type); break; } } } int main(int ac, char *av[]){ if(2!=ac) die("usage: %s \n", av[0]); switch(inet_pton(AF_INET, av[1], &(ping.pingaddr.sin_addr))){ default: diesys("inet_pton"); case 0: die("expected a numeric ip address e.g. 127.0.0.1\n"); case 1: break; } int sock = create_icmp_sock(); send_ping(sock); await_pingback(sock); if(close(sock)<0) diesys("close socket"); return 0; }