/* * EMULAB-COPYRIGHT * Copyright (c) 2000-2002 University of Utah and the Flux Group. * All rights reserved. */ /* * Send the magical ping of death ICMP type */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IPOD_ICMPTYPE 6 #define IPOD_ICMPCODE 6 #define IPOD_IPLEN 666 #define IPOD_IDLEN 32 int icmpid = 0; static char myid[IPOD_IDLEN]; static int myidlen = 0; u_short in_cksum(u_short *addr, int len); void icmpmap_init(); /* For getting information */ void icmp_info(struct icmp *icmp, char *outbuf, int maxlen); /* * We perform lookups on the hosts, and then store them in a chain * here. */ struct hostdesc { char *hostname; struct in_addr hostaddr; struct hostdesc *next; }; struct hostdesc *hostnames; struct hostdesc *hosttail; /* * Set up the list of hosts. Return the count. */ int makehosts(char **hostlist) { int i; struct hostent *hp; struct in_addr tmpaddr; int hostcount = 0; for (i = 0; hostlist[i]; i++) { #ifdef DEBUG printf("Resolving %s\n", hostlist[i]); #endif if (!hostlist[i] || !hostlist[i][0] || strlen(hostlist[i]) > MAXHOSTNAMELEN) { fprintf(stderr, "bad host entry, exiting\n"); exit(-1); } if (!inet_aton(hostlist[i], &tmpaddr)) { if ((hp = gethostbyname(hostlist[i])) == NULL) { /* Could not resolve it. Skip it. */ fprintf(stderr, "%s: unknown host\n", hostlist[i]); continue; } else { memcpy(&tmpaddr.s_addr, hp->h_addr_list[0], hp->h_length); } } /* The host has been resolved. Put it in the chain */ /* We want to stick it on the end. */ if (hostnames == NULL) { hostnames = (struct hostdesc *) malloc(sizeof(*hostnames)); if (hostnames == NULL) { perror("hostnames malloc failed"); exit(-1); } hosttail = hostnames; } else { hosttail->next = (struct hostdesc *) malloc(sizeof(*hostnames)); if (hosttail->next == NULL) { perror("hosttail->next malloc failed"); exit(-1); } hosttail = hosttail->next; } hosttail->hostname = strdup(hostlist[i]); if (hosttail->hostname == NULL) { perror("strdup failed"); exit(-1); } hosttail->hostaddr = tmpaddr; hosttail->next = NULL; hostcount++; } return hostcount; } void usage(char *prog) { fprintf(stderr, "%s [ -i identityfile ] target [ target ... ]\n", prog); } /* * Set up a packet. Returns the length of the ICMP portion. */ void initpacket(char *buf, int querytype, struct in_addr fromaddr) { struct ip *ip = (struct ip *)buf; struct icmp *icmp = (struct icmp *)(ip + 1); /* things we customize */ int icmplen = 0; ip->ip_src = fromaddr; /* if 0, have kernel fill in */ ip->ip_v = 4; /* Always use ipv4 for now */ ip->ip_hl = sizeof *ip >> 2; ip->ip_tos = 0; ip->ip_id = htons(4321); ip->ip_ttl = 255; ip->ip_p = 1; ip->ip_sum = 0; /* kernel fills in */ icmp->icmp_seq = 1; icmp->icmp_cksum = 0; /* We'll compute it later. */ icmp->icmp_type = querytype; icmp->icmp_code = IPOD_ICMPCODE; if (myidlen) memcpy(icmp->icmp_data, myid, myidlen); ip->ip_len = IPOD_IPLEN; icmplen = IPOD_IPLEN - sizeof(struct ip); icmp->icmp_cksum = in_cksum((u_short *)icmp, icmplen); } /* * Send all of the ICMP queries. */ void sendpings(int s, int querytype, struct hostdesc *head, int delay, struct in_addr fromaddr) { char buf[1500]; struct ip *ip = (struct ip *)buf; struct sockaddr_in dst; bzero(buf, 1500); initpacket(buf, querytype, fromaddr); dst.sin_family = AF_INET; #ifdef DA_HAS_SIN_LEN dst.sin_len = sizeof(dst); #endif while (head != NULL) { int rc; #ifdef DEBUG printf("pinging %s\n", head->hostname); #endif ip->ip_dst.s_addr = head->hostaddr.s_addr; dst.sin_addr = head->hostaddr; rc = sendto(s, buf, ip->ip_len, 0, (struct sockaddr *)&dst, sizeof(dst)); if (rc != ip->ip_len) { perror("sendto"); } /* Don't flood small pipes. */ if (delay) usleep(delay); head = head->next; } } /* * Handles our timeout for us. Called by the signal handler * when we get a SIGARLM. */ void myexit(int whatsig) { exit(0); } /* * Open a raw socket for receiving ICMP. Tell the kernel we want * to supply the IP headers. */ int get_icmp_socket() { int s; int on = 1; if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) { perror("socket"); exit(1); } if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, (const char *)&on, sizeof(on)) < 0) { perror("IP_HDRINCL"); exit(1); } return s; } int main(int argc, char **argv) { int s; char *progname; extern char *optarg; /* getopt variable declarations */ extern int optind; char ch; /* Holds the getopt result */ int hostcount; int delay = 0; int querytype = ICMP_TSTAMP; struct in_addr fromaddr; int timeout = 5; /* Default to 5 seconds */ int identityfile; fromaddr.s_addr = 0; progname = argv[0]; querytype = IPOD_ICMPTYPE; /* the magical death packet number */ while ((ch = getopt(argc, argv, "i:")) != -1) switch(ch) { case 'i': if (optarg[0] == '-') identityfile = 0; else if ((identityfile = open(optarg, 0)) < 0) { perror(optarg); exit(1); } myidlen = read(identityfile, myid, IPOD_IDLEN); if (optarg[0] != '-') close(identityfile); if (myidlen != IPOD_IDLEN) { fprintf(stderr, "%s: cannot read %d-byte identity\n", optarg[0] != '-' ? optarg : "", IPOD_IDLEN); exit(2); } break; default: usage(progname); exit(-1); } argc -= optind; argv += optind; if (!argv[0] || !strlen(argv[0])) { usage(progname); exit(-1); } hostcount = makehosts(argv); s = get_icmp_socket(); signal(SIGALRM, myexit); alarm(timeout); sendpings(s, querytype, hostnames, delay, fromaddr); exit(0); } /* * in_cksum -- * Checksum routine for Internet Protocol family headers (C Version) * From FreeBSD's ping.c */ u_short in_cksum(addr, len) u_short *addr; int len; { register int nleft = len; register u_short *w = addr; register int sum = 0; u_short answer = 0; /* * Our algorithm is simple, using a 32 bit accumulator (sum), we add * sequential 16 bit words to it, and at the end, fold back all the * carry bits from the top 16 bits into the lower 16 bits. */ while (nleft > 1) { sum += *w++; nleft -= 2; } /* mop up an odd byte, if necessary */ if (nleft == 1) { *(u_char *)(&answer) = *(u_char *)w ; sum += answer; } /* add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return(answer); }