/* * The Towers Of Hanoi * ping (ICMP) implementation * Copyright (C) 2002 Amit Singh. All Rights Reserved. * http://hanoi.kernelthread.com * * Tested on RedHat Linux 8.0, kernel 2.4.18 * * a) Compile the program by simply doing: * * gcc -o hanoicmp hanoicmp.c * * b) Disable the "normal" ICMP echo response in the kernel by doing: * * echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all * * Thereafter you should not be able to ping the host and get a response. * * c) Start hanoicmp, which would act as an (single threaded - not too useful * for production use) ICMP server. You should be able to ping the host * and get a response. You should certainly terminate hanoicmp and enable * ICMP echo (if you want it) in /proc/sys. * * d) Now you can "ping" this host (either from localhost or a remote machine) * and the ping should "work" normally. However, if you use the TOS (type * of service) option of your ping command to pass a TOS value, this value * will be treated as N, the number of disks for the Hanoi puzzle. If N * happens to fall within acceptable limits (say, between 1 and 10, both * inclusive), then instead of getting your usual ICMP echo replies, you * will get a ping packet for each move of the puzzle, with the sequence * number of the packet being in the following format: * * pmn * * m and n are single digit numbers, one of 1, 2 or 3. mn represents * a move from tower 'm' to tower 'n'. Thus, the three towers in the * puzzle are '1', '2' and '3', with the source tower being '1' and * the destination tower being '3'. * * p is an integer representing the number of the move. * * For example: pmn = 113 means: * * p = 1, it's the first move * m = 1, move the disk from tower '1' ... * n = 3, ... to tower '3' * * e) The way to ping (on the client side) is as follows: * * Linux: * * ping -Q N hostname * * Solaris: * * ping -P N' hostname * * There is one final complication. N is the number of disks in the * puzzle if you are pinging from a little endian machine. So, Linux * on x86 is fine, and you can do "ping -Q 3 hostname" to solve a * 3 disk puzzle. * * However, if you are pinging from SPARC Solaris (big endian), you * must use N' as the number of disks + 128. Thus, to solve the same * 3 disk puzzle from Solaris you would do "ping -P 131 -s hostname". */ /* Maintain sanity and integrity: 10 disks is enough. */ #define MAXDISKS 10 #ifndef linux #error "*** Must be compiled on Linux" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_MTU 1500 /* random */ unsigned short in_cksum(unsigned short *addr, int len); /* From Stevens, UNP2ev1 */ unsigned short in_cksum(unsigned short *addr, int len) { int nleft = len; int sum = 0; unsigned short *w = addr; unsigned short answer = 0; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(unsigned char *)(&answer) = *(unsigned char *)w; sum += answer; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return (answer); } int main(int argc, char **argv) { /* ICMP variables */ int ret = 0; int one = 1; int endian = 0; int done_id = 0; int sock_eth, sock_icmp; struct sockaddr_in dst; struct ether_header *eth_hdr; struct ip *ip_hdr_in, *ip_hdr_out; char buf_in[MAX_MTU], buf_out[MAX_MTU]; struct icmp *icmp_hdr_in, *icmp_hdr_out; int ip_len, icmp_len, icmp_data_len; /* hanoi variables */ u_int8_t N; int sp = 0; int *STACK; char MOVE[MAXDISKS]; int move = 0, gmove = 0; int proc, to, using, from; if ((STACK = (int *)malloc(1 << MAXDISKS)) == NULL) { perror("malloc"); exit(1); } if ((sock_eth = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL))) < 0) { perror("socket"); exit(1); } if ((sock_icmp = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) { perror("socket"); exit(1); } if ((ret = setsockopt(sock_icmp, IPPROTO_IP, IP_HDRINCL, (char *)&one, sizeof(one))) < 0) { perror("setsockopt"); exit(1); } eth_hdr = (struct ether_header *)buf_in; ip_hdr_in = (struct ip *)(buf_in + sizeof(struct ether_header)); icmp_hdr_in = (struct icmp *)((unsigned char *)ip_hdr_in + sizeof(struct ip)); ip_hdr_out = (struct ip *)buf_out; icmp_hdr_out = (struct icmp *)(buf_out + sizeof(struct ip)); while(1) { /* ICMP processing loop */ if ((ret = recv(sock_eth, buf_in, sizeof(buf_in), 0)) < 0) { perror("recv"); exit(1); } if (ip_hdr_in->ip_p == IPPROTO_ICMP) { if (icmp_hdr_in->icmp_type == ICMP_ECHO) { /* Prepare outgoing IP header. */ ip_hdr_out->ip_v = ip_hdr_in->ip_v; ip_hdr_out->ip_hl = ip_hdr_in->ip_hl; ip_hdr_out->ip_tos = ip_hdr_in->ip_tos; ip_hdr_out->ip_tos = 0; ip_hdr_out->ip_len = ip_hdr_in->ip_len; ip_hdr_out->ip_id = ip_hdr_in->ip_id; ip_hdr_out->ip_off = 0; ip_hdr_out->ip_ttl = 255; ip_hdr_out->ip_p = IPPROTO_ICMP; ip_hdr_out->ip_sum = 0; ip_hdr_out->ip_src.s_addr = ip_hdr_in->ip_dst.s_addr; ip_hdr_out->ip_dst.s_addr = ip_hdr_in->ip_src.s_addr; ip_hdr_out->ip_sum = in_cksum((unsigned short *)buf_out, ip_hdr_out->ip_hl); /* Prepare outgoing ICMP header. */ icmp_hdr_out->icmp_type = 0; icmp_hdr_out->icmp_code = 0; icmp_hdr_out->icmp_cksum = 0; icmp_hdr_out->icmp_id = icmp_hdr_in->icmp_id; icmp_hdr_out->icmp_seq = icmp_hdr_in->icmp_seq; /* Don't send any more replies if hanoi done. */ if (done_id == icmp_hdr_in->icmp_id) { continue; } ip_len = ntohs(ip_hdr_out->ip_len); icmp_len = ip_len - sizeof(struct iphdr); icmp_data_len = icmp_len - sizeof(struct icmphdr); printf("ICMP_ECHO request.\n"); memcpy(icmp_hdr_out->icmp_data, icmp_hdr_in->icmp_data, icmp_data_len); icmp_hdr_out->icmp_cksum = in_cksum((unsigned short *)icmp_hdr_out, icmp_len); bzero(&dst, sizeof(dst)); dst.sin_family = AF_INET; dst.sin_addr.s_addr = ip_hdr_out->ip_dst.s_addr; /* Determine if this is a hanoi packet. */ N = ip_hdr_in->ip_tos; endian = N >> 7; N -= (N & 0x80); if ((N <= 0) || (N > MAXDISKS)) { ret = sendto(sock_icmp, buf_out, ip_len, 0, (struct sockaddr *)&dst, sizeof(dst)); if (ret < 0) { perror("sendto"); } } else { /* hanoi. */ STACK[sp++] = N; STACK[sp++] = 1; STACK[sp++] = 2; STACK[sp++] = 3; STACK[sp++] = 0; gmove = 0; while (sp > 0) { proc = STACK[--sp]; to = STACK[--sp]; using = STACK[--sp]; from = STACK[--sp]; N = STACK[--sp]; if (proc == 0) { if (N == 1) { sprintf(MOVE, "%d%d%d", ++gmove, from, to); move = atoi(MOVE); /* no errors */ if (endian) { move = htons(move); } icmp_hdr_out->icmp_seq = move; icmp_hdr_out->icmp_cksum = 0; icmp_hdr_out->icmp_cksum = in_cksum((unsigned short *)icmp_hdr_out, icmp_len); ret = sendto(sock_icmp, buf_out, ip_len, 0, (struct sockaddr *)&dst, sizeof(dst)); if (ret < 0) { perror("sendto"); } } else { STACK[sp++] = N; STACK[sp++] = from; STACK[sp++] = using; STACK[sp++] = to; STACK[sp++] = 1; STACK[sp++] = N - 1; STACK[sp++] = from; STACK[sp++] = to; STACK[sp++] = using; STACK[sp++] = 0; } } else { sprintf(MOVE, "%d%d%d", ++gmove, from, to); move = atoi(MOVE); /* no errors */ if (endian) { move = htons(move); } icmp_hdr_out->icmp_seq = move; icmp_hdr_out->icmp_cksum = 0; icmp_hdr_out->icmp_cksum = in_cksum((unsigned short *)icmp_hdr_out, icmp_len); ret = sendto(sock_icmp, buf_out, ip_len, 0, (struct sockaddr *)&dst, sizeof(dst)); if (ret < 0) { perror("sendto"); } STACK[sp++] = N - 1; STACK[sp++] = using; STACK[sp++] = from; STACK[sp++] = to; STACK[sp++] = 0; } } done_id = icmp_hdr_in->icmp_id; } } } } close(sock_eth); close(sock_icmp); } /* __END__ */