#include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #include <signal.h> #include <sys/socket.h> #include <netdb.h> #include <arpa/inet.h> #include <netinet/ip_icmp.h> #define DEFDATALEN 12 #define MAXIPLEN 60 #define MAXICMPLEN 76 #define MAXPACKET (65536 - 60 - 8) char *hostname; int sock_icmp; struct addrinfo *addrinfo; int datalen = DEFDATALEN; uint8_t outpack[MAXPACKET]; int response = 0; void sender(void); uint16_t calc_cksum(uint16_t *data, int len); void procpack(uint8_t *data, int len, struct sockaddr_in *from); void sig_alrm(int signo); int main(int argc, char *argv[]) { int res; struct addrinfo *item; struct addrinfo hints; if (argc != 2) { fprintf(stderr, "Usage: %s destination\n", argv[0]); exit(EXIT_FAILURE); } memset(&hints, 0, sizeof hints); hints.ai_flags = AI_CANONNAME; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_RAW; hints.ai_protocol = IPPROTO_ICMP; if ((res = getaddrinfo(argv[1], NULL, &hints, &addrinfo)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(res)); exit(EXIT_FAILURE); } item = addrinfo; struct sockaddr_in *addr; char ip[INET_ADDRSTRLEN]; while (item) { addr = (struct sockaddr_in *)item->ai_addr; inet_ntop(item->ai_family, &addr->sin_addr, ip, INET_ADDRSTRLEN); printf( "\n" "ai_flags = %d\n" "ai_family = %d\n" "ai_socktype = %d\n" "ai_protocol = %d\n" "ai_addrlen = %d\n" "ai_addr.sin_family = %d, ai_addr.sin_port = %d, ai_addr.sin_addr.s_addr = %s\n" "ai_canonname = %s\n", item->ai_flags, item->ai_family, item->ai_socktype, item->ai_protocol, item->ai_addrlen, addr->sin_family, addr->sin_port, ip, item->ai_canonname); item = item->ai_next; } if ((sock_icmp = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol)) < 0) { perror("socket"); exit(EXIT_FAILURE); } sender(); int packlen = datalen + MAXICMPLEN + MAXIPLEN; uint8_t *packet = (uint8_t *)malloc(packlen); if (!packet) { fprintf(stderr, "malloc packet"); exit(EXIT_FAILURE); } signal(SIGALRM, sig_alrm); alarm(5); for (;;) { struct sockaddr_in from; socklen_t fromlen = sizeof from; int n; if ((n = recvfrom(sock_icmp, (char *)packet, packlen, 0, (struct sockaddr *)&from, &fromlen)) < 0) { if (errno == EINTR) { printf("recvfrom: errno = EINTR\n"); continue; } perror("recvfrom"); continue; } procpack(packet, n, &from); } return 0; } void sender(void) { struct icmp *icp; int n; icp = (struct icmp *)outpack; icp->icmp_type = ICMP_MASKREQ; icp->icmp_code = 0; icp->icmp_cksum = 0; // will calc checksum below icp->icmp_id = getpid(); icp->icmp_seq = 12345; icp->icmp_mask = 0; // calc ICMP checksum here // ICMP_MASKLEN: 12 = 8 bytes of header, 4 bytes of mask icp->icmp_cksum = calc_cksum((uint16_t *)icp, ICMP_MASKLEN); n = sendto(sock_icmp, outpack, ICMP_MASKLEN, 0, addrinfo->ai_addr, addrinfo->ai_addrlen); if (n < 0 || n != ICMP_MASKLEN) { if (n < 0) { perror("sendto"); } else { fprintf(stderr, "wrote %s %d chars, ret=%d\n", addrinfo->ai_canonname, ICMP_MASKLEN, n); } exit(EXIT_FAILURE); } } uint16_t calc_cksum(uint16_t *data, int len) { int nleft = len; uint16_t *w = data; int sum = 0; uint16_t answer = 0; while (nleft > 1) { sum += *w; w++; nleft -= 2; } if (nleft == 1) { *(u_int8_t *)(&answer)= *(uint8_t *)w; sum += answer; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return answer; } void procpack(uint8_t *data, int len, struct sockaddr_in *from) { struct ip *ip; struct icmp *icp; int hlen; char fromIP[INET_ADDRSTRLEN]; char mask[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &from->sin_addr, fromIP, INET_ADDRSTRLEN); ip = (struct ip *)data; hlen = ip->ip_hl * 4; if (len < hlen + ICMP_MINLEN) { fprintf(stderr, "packet too short (%d bytes) from %s", len, fromIP); return; } len -= hlen; icp = (struct icmp *)(data + hlen); if (icp->icmp_type == ICMP_MASKREPLY) { if (len != ICMP_MASKLEN) { printf("len = %d, expected len = %d\n", len, ICMP_MASKLEN); return; } if (icp->icmp_seq != 12345) { printf("received sequence # %d\n", icp->icmp_seq); return; } if (icp->icmp_id != getpid()) { printf("received id %d\n", icp->icmp_id); return; } inet_ntop(AF_INET, &icp->icmp_mask, mask, INET_ADDRSTRLEN); printf("received mask = %08x (%s), from %s\n", ntohl(icp->icmp_mask), mask, fromIP); response++; } } void sig_alrm(int signo) { if (response == 0) { printf("timeout\n"); exit(1); } exit(0); }