/* ** Name - iploonie.c ** Description - Random packet generator ** Author - Arrigo Triulzi ** Copyright - (c) 2002, 2003, 2004, 2005 Arrigo Triulzi ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ** ** [Full license: http://www.gnu.org/licenses/gpl.html] ** ** Note - Currently only tested under Linux, OpenBSD and OS X ** ** $Id: iploonie.c,v 1.23 2005/02/18 12:15:48 arrigo Exp $ */ #if defined(__APPLE_CC__) || defined(__OpenBSD__) # define BSDISM #endif #include #include #include #include #include #include #include #include #include #if defined(BSDISM) # include #endif /* BSDISM */ #include #include #include #include #if defined(__OpenBSD__) # include # include #else # include #endif /* __OpenBSD */ #include #include #include #include #include #include #define DEFAULT_SNAP 1500 #define INETMAX 16 #define ETHERLEN 14 #define IPV4LEN 20 #define IPV6LEN 40 #define DADDROFFSET 16 #define CHKSUMOFFSET 10 #define IPV6SRCOFFSET 8 #define IPV6DSTOFFSET 24 #define DEVRANDOM "/dev/urandom" #if !defined(ETH_ALEN) && defined(ETHER_ADDR_LEN) # define ETH_ALEN ETHER_ADDR_LEN #endif /* !ETH_ALEN && ETHER_ADDR_LEN */ #if !defined(ETH_P_IPV6) && !defined(ETHERTYPE_IPV6) # define ETH_P_IPV6 0x88DD #endif /* !defined(ETH_P_IPV6) */ #if defined(ETHERTYPE_IPV6) # define ETH_P_IPV6 ETHERTYPE_IPV6 #endif /* defined(ETHERTYPE_IPV6) */ const char *progname = "IPloonie - a random packet generator"; const char *version = "$Revision: 1.23 $"; const char *build = __DATE__; const char *copyright = "(c) 2002-2005, Arrigo Triulzi"; extern char *optarg; extern int optind,opterr,optopt; static int debug; static int verbose; /* ** IP checksum (from RFC 1071) */ inline unsigned short ipchecksum(unsigned short *addr, int count); /* ** Warning: assumes long = 4 bytes! */ union l2b { long num; char byte[4]; }; int main(int argc, char **argv) { char *outfile, *rnddev; char c; u_int8_t dhost[ETH_ALEN]; char *randstate; FILE *out, *rnd; struct pcap_pkthdr pcap_hdr; struct pcap_file_header pcap_fhdr; int counter, npkt, pktlen, rndlen, internal_rnd; int force, ip_mode, cksum, ipv4, ipv6, ether_ip4; int tcpdump_kludge; int snaplen, hlen, ip_hlen; int neededrnd, missing, i, j; u_char *packet; unsigned short ipchksum; struct ether_header eth_hdr; struct timeval tval; char sdaddr[INETMAX]; struct in_addr ipdaddr; unsigned long daddr; union l2b urnd; struct ip6_hdr ip6hdr; struct in6_addr ip6src, ip6dst; outfile = (char *) NULL; rnddev = (char *) NULL; out = (FILE *) NULL; rnd = (FILE *) NULL; verbose = 0; debug = 0; force = 0; npkt = 0; rndlen = 0; pktlen = 0; internal_rnd = 0; ip_mode = 0; ether_ip4 = 0; tcpdump_kludge = 0; snaplen = 0; neededrnd = 0; missing = 0; cksum = 0; ipchksum = (unsigned short) 0; ipv4 = 0; ipv6 = 0; ip_hlen = 0; while((c=getopt(argc,argv,"ho:vdfn:s:r:litEIc46")) != EOF) { switch(c) { case 'o': if(!optarg) { fprintf(stderr,"%s: missing filename\n", argv[0]); exit(1); } if((outfile = (char *) calloc(strlen(optarg)+1,sizeof(char))) == NULL) { perror(argv[0]); exit(1); } strncpy(outfile,optarg,strlen(optarg)); break; case 'r': if(!optarg) { fprintf(stderr,"%s: missing filename\n", argv[0]); exit(1); } if((rnddev = (char *) calloc(strlen(optarg)+1,sizeof(char))) == NULL) { perror(argv[0]); exit(1); } strncpy(rnddev,optarg,strlen(optarg)); break; case 'n': if(!optarg) { fprintf(stderr,"%s: missing number of packets to write\n", argv[0]); exit(1); } npkt = atoi(optarg); break; case 's': if(!optarg) { fprintf(stderr,"%s: missing snaplen to use\n", argv[0]); exit(1); } snaplen = atoi(optarg); break; case 'f': /* Allow file overwriting ("force") */ force = 1; break; case 'i': /* Allow file overwriting ("force") */ internal_rnd = 1; break; case 'v': /* Note allow multiple v's to increase verbosity */ ++verbose; break; case 'd': ++debug; if (!verbose) { verbose += 10; /* Make sure we woffle! */ } break; case 'l': rndlen = 1; break; case 't': tcpdump_kludge = 1; break; case 'I': ip_mode = 1; break; case 'E': ether_ip4 = 1; break; case 'c': cksum = 1; break; case '4': ipv4 = 1; break; case '6': ipv6 = 1; break; default: case 'h': fprintf(stderr,"%s\n%s (build: %s)\n%s\n\n", progname, version, build, copyright); fprintf(stderr,"usage: %s %s", argv[0], "[-vfdlitIEc] -o outfile -n num [-s snap] [-r rnddev]\n"); fprintf(stderr,"\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", "-v\t\tverbose output\n", "-f\t\tforce overwriting of output file\n", "-d\t\tdebugging output\n", "-I\t\tIP mode, IP header src & dst is valid\n", "-l\t\trandomise length of packets between 0 and snap\n", "-i\t\tuse internal random() instead of device file\n", "-c\t\tcalculate correct IP checksums\n", "-4\t\tforce IPv4 in the IP header\n", "-6\t\tforce IPv6 in the IP header\n", "-E\t\tforce ethernet type to be 0x800 even for IPv6\n", "-t\t\ttcpdump kludge for IPv6 for \"proto ipv6\" opt.\n", "-o outfile\tset the pcap output to go to outfile\n", "-n num\t\tnumber of packets to be written\n", "-s snap\t\tsnap length, defaults to 1500\n", "-r rnddev\tfile/device from which to take randomness\n"); fprintf(stderr,"\n%s%s%s%s%s", "Note: if you choose a device, eg. /dev/urandom, and it\n", "runs out of randomness then you will get a SEGV. This\n", "actually takes place in the fread call, not this code\n", "proper. The fix is either to use -i or a sufficiently\n", "random file, eg. an ISO image.\n"); fprintf(stderr,"\n%s%s%s%s%s%s%s%s", "How to get a HW address for the destination... well,\n", "very simply do a \"ping victim\", then look at your\n", "own ARP table with \"arp -a\" and grab the set of hex\n", "numbers separated by ':' in the output for the machine\n", "you just pinged.\n\n", "You need a destination HW address, o/w the packets can\n", "not reach their destination. You could use the b/cast\n", "address of FF:FF:FF:FF:FF:FF but I don't recommend it.\n" ); exit(0); break; } } if(!outfile) { fprintf(stderr,"%s: no output file defined\n", argv[0]); exit(1); } if(!npkt) { fprintf(stderr,"%s: how many packets do you want?\n", argv[0]); exit(1); } if(!snaplen) { snaplen = DEFAULT_SNAP; } if(cksum && !ip_mode) { fprintf(stderr,"%s: checksum mode without IP mode makes no sense\n", argv[0]); exit(1); } if(ipv4 && !ip_mode) { fprintf(stderr,"%s: IPv4 mode without IP mode makes no sense\n", argv[0]); exit(1); } if(ipv6 && !ip_mode) { fprintf(stderr,"%s: IPv6 mode without IP mode makes no sense\n", argv[0]); exit(1); } if(ipv4 && ipv6) { fprintf(stderr,"%s: IPv6 mode and IPv4 mode are mutually exclusive\n", argv[0]); exit(1); } if(ipv4 && ether_ip4) { fprintf(stderr,"%s: forcing Ethernet type to 0x800 is for IPv6 only\n", argv[0]); exit(1); } if(!ipv6 && tcpdump_kludge) { fprintf(stderr,"%s: tcpdump kludge is for IPv6 only\n", argv[0]); exit(1); } if(!rnddev && !internal_rnd) { if((rnddev = (char *) calloc(strlen(DEVRANDOM)+1,sizeof(char))) == NULL) { perror(argv[0]); exit(1); } strncpy(rnddev,DEVRANDOM,strlen(DEVRANDOM)); } if(!access(outfile,F_OK) && !force) { fprintf(stderr, "%s: file \"%s\" exists, might want to use -f option.\n", argv[0], outfile); exit(1); } /* ** Device file will fail the access() test, so we check for /dev ** in the name. */ if(!access(rnddev,R_OK) && !strstr(rnddev,"/dev/")) { fprintf(stderr, "%s: random source \"%s\" is unreadable, use -r to redefine\n", argv[0], rnddev); exit(1); } if(verbose) { fprintf(stdout,"%s\n%s (build %s)\n%s\n\n", progname, version, build, copyright); fprintf(stdout,"Using output file: %s\n", outfile); if(internal_rnd) { fprintf(stdout,"Using libc random()\n\n"); } else { fprintf(stdout,"Using randomness source: %s\n\n", rnddev); } } /* ** Fill in the pcap file header, note that there is a certain ** amount of "creativity" in this */ pcap_fhdr.version_major = PCAP_VERSION_MAJOR; pcap_fhdr.version_minor = PCAP_VERSION_MINOR; pcap_fhdr.magic = 2712847316UL; pcap_fhdr.thiszone = 0; pcap_fhdr.sigfigs = 0; pcap_fhdr.snaplen = ( snaplen ? snaplen : DEFAULT_SNAP ); pcap_fhdr.linktype = DLT_EN10MB; if(debug) { fprintf(stderr,"Fake pcap file header\n"); fprintf(stderr, "pcap: ver %d.%d magic %x tz %d prec %u snap %u link %u\n", pcap_fhdr.version_major, pcap_fhdr.version_minor, pcap_fhdr.magic, pcap_fhdr.thiszone, pcap_fhdr.sigfigs, pcap_fhdr.snaplen, pcap_fhdr.linktype); } if((out = fopen(outfile,"wb+")) == NULL) { perror(argv[0]); exit(1); } fwrite(&pcap_fhdr,sizeof(struct pcap_file_header), (size_t) 1, out); fflush(out); if(!internal_rnd) { if((rnd = fopen(DEVRANDOM,"rb")) == NULL) { perror(argv[0]); exit(1); } } else { if((randstate = (char *) calloc(256, sizeof(char))) == NULL) { perror(argv[0]); exit(1); } initstate((unsigned int) time(NULL), randstate, 256); } /* ** Prepare fake Ethernet header, note that for it to make ** sense you need the destination hardware address. This is ** because the only way to get rubbish to the machine is to make ** make sure the receiving end is forced to pick it up. ** ** As a source Ethernet address we use the world standard... it makes ** no difference since chances are nothing will ever come back! ** ** 00:DE:AD:BE:EF:00 ** ** This choice also makes it an easy IDS signature if the program ** is used maliciously. ** ** For the destination we have to ask... */ printf("Destination HW address in hex (eg. 00:d0:59:13:d5:86): "); scanf("%2hx:%2hx:%2hx:%2hx:%2hx:%2hx", &dhost[0], &dhost[1], &dhost[2], &dhost[3], &dhost[4], &dhost[5]); if(debug) { fprintf(stderr,"Entered HW address: %02X:%02X:%02X:%02X:%02X:%02X\n", dhost[0], dhost[1], dhost[2], dhost[3], dhost[4], dhost[5]); } memcpy(ð_hdr.ether_dhost,&dhost,6*sizeof(u_int8_t)); eth_hdr.ether_shost[0] = 0x00; eth_hdr.ether_shost[1] = 0xde; eth_hdr.ether_shost[2] = 0xad; eth_hdr.ether_shost[3] = 0xbe; eth_hdr.ether_shost[4] = 0xef; eth_hdr.ether_shost[5] = 0x00; eth_hdr.ether_type = (ether_ip4 ? htons(ETHERTYPE_IP) : (ipv6 ? htons(ETH_P_IPV6) : htons(ETHERTYPE_IP))); if(debug) { fprintf(stderr,"Ethernet type: %X\n", eth_hdr.ether_type); } /* ** We fill in a few fields of the IP header, in practice the source ** and destination address */ if(ip_mode && ipv4) { printf("Destination IP address in dotted quad (eg. 127.0.0.1): "); scanf("%15s", sdaddr); if(debug) { fprintf(stderr,"Entered IP address: %s\n", sdaddr); } if(inet_aton(sdaddr, &ipdaddr) == 0) { fprintf(stderr,"%s: invalid IP address entered\n", argv[0]); exit(1); } /* ** Weird, sticking htonl() flips the address... perhaps inet_aton() ** offers the service for free */ daddr = ipdaddr.s_addr; } counter=0; ip_hlen = (ipv6 ? IPV6LEN : IPV4LEN); if(verbose && snaplen < (ETHERLEN+ip_hlen) && ip_mode) { printf("Warning: snap %d < |Eth hdr|+|IP hdr|, increasing to %d\n", snaplen, (ETHERLEN+ip_hlen)); } while(counter < npkt) { if(rndlen) { pktlen = rand() % snaplen ; } else { pktlen = snaplen; } if(pktlen < (ETHERLEN+ip_hlen) && ip_mode) { /* No IP header can fit... */ pktlen = ETHERLEN+ip_hlen; if(debug) { fprintf(stderr,"Punt! random pktlen too short: set to %d\n", pktlen); } } if(pktlen < ETHERLEN && !ip_mode) { /* We need at least a hardware layer */ pktlen = ETHERLEN; if(debug) { fprintf(stderr,"Punt! random pktlen too short: set to %d\n", pktlen); } } if((packet = calloc(pktlen,sizeof(u_char))) == NULL) { perror(argv[0]); exit(1); } memcpy(packet, ð_hdr, sizeof(struct ether_header)); if(!internal_rnd) { fread((packet+ETHERLEN), sizeof(u_char), (size_t) (pktlen-ETHERLEN), rnd); } else { neededrnd = (pktlen-ETHERLEN)/4; /* let truncation do its job */ missing = (pktlen-ETHERLEN) - neededrnd*4; if(debug) { fprintf(stderr,"Packet %d, len %d, needed %d, missing %d\n", counter, pktlen, neededrnd, missing); } j = ETHERLEN; for(i=0;i (pktlen-ETHERLEN)/4) { if(debug) { fprintf(stderr, "Punt! hlen %u too big, shrinking to %u.\n", hlen, ip_hlen/4); } *(packet+ETHERLEN) &= 0xf0; /* preserve IP version */ *(packet+ETHERLEN) |= (ip_hlen/4); } else if(hlen < (ip_hlen/4)) { if(debug) { fprintf(stderr, "Punt! hlen %u too small, growing to %u.\n", hlen, ip_hlen/4); } *(packet+ETHERLEN) &= 0xf0; /* preserve IP version */ *(packet+ETHERLEN) |= (ip_hlen/4); } if(debug) { fprintf(stderr,"IP byte 0: %#hhx\n", *(packet+ETHERLEN)); } /* ** Calculate the IP checksum and stick it in the correct ** place. This pretty much ensures that some stacks will ** process the packet... */ if(cksum) { memset((packet+ETHERLEN+CHKSUMOFFSET),0,2); ipchksum = ipchecksum((unsigned short *)(packet+ETHERLEN), (*(packet+ETHERLEN)&0x0f)*4); memcpy((packet+ETHERLEN+CHKSUMOFFSET), &ipchksum, sizeof(unsigned short)); if(debug) { fprintf(stderr,"IP checksum: %#hx\n", ipchksum); } } } if(ip_mode && ipv6) { /* ** Ensure that the IP version is 6 and all other bits & bobs ** match the IPv6 "standard du jour". ** ** 1 1 2 3 ** 0 4 8 2 6 4 2 ** |vers| trf|cls | flo|w la|bel | ** */ *(packet+ETHERLEN) = 0x0; /* Set tc top nibble to zero */ *(packet+ETHERLEN) |= (6<<4); /* Push version number */ /* ** As a signature we use a flow label (20 bits) of ** 0x012345 = 'ABC'. Note that top nibble is zero as it ** represents the bottom nibble of the traffic class... */ *(packet+ETHERLEN+1) = 0x01; *(packet+ETHERLEN+2) = 0x23; *(packet+ETHERLEN+3) = 0x45; /* ** Mystery of faith: tcpdump calls an ip6 packet anything ** which has a 0x29 at offset 23 into the packet if the ** Ethernet type is 0x0800 or at offset 20 if the type is ** 0x86dd... according to the following tcpdump -d output: ** ** (000) ldh [12] ** (001) jeq #0x800 jt 2 jf 4 ** (002) ldb [23] ** (003) jeq #0x29 jt 7 jf 8 ** (004) jeq #0x86dd jt 5 jf 8 ** (005) ldb [20] ** (006) jeq #0x29 jt 7 jf 8 ** (007) ret #1500 ** (008) ret #0 ** ** Note that the offset is from the beginning of the ** Ethernet frame, so we are talking offset 9 and 6 resp. of ** the IPv6 header. This forces the "next header" to be ** IPv6... */ if(tcpdump_kludge) { if(ether_ip4) { *(packet+23) = 0x29; } else { *(packet+20) = 0x29; } } if(debug) { /* IPv6 Decoding Services Ltd. */ memcpy(&ip6hdr,(packet+ETHERLEN),sizeof(struct ip6_hdr)); fprintf(stderr,"IP6: done\n"); } } pcap_hdr.caplen = pktlen; pcap_hdr.len = pktlen; gettimeofday(&tval, NULL); pcap_hdr.ts.tv_sec = tval.tv_sec; pcap_hdr.ts.tv_usec = tval.tv_usec; fwrite(&pcap_hdr, sizeof(struct pcap_pkthdr), (size_t) 1, out); fwrite(packet, sizeof(u_char), (size_t) pcap_hdr.caplen, out); free(packet); ++counter; } if(!internal_rnd) { fclose(rnd); } fclose(out); if(debug) { fprintf(stderr,"Written %d packets to %s.\n", --counter, outfile); } return(0); } /* ** Internet checksum calculation (from RFC1071) ** ** Compute Internet Checksum for "count" bytes ** beginning at location "addr". */ inline unsigned short ipchecksum(unsigned short *addr, int count) { unsigned long sum = 0; while(count > 1) { /* This is the inner loop */ sum += *addr++; count -= 2; } /* Add left-over byte, if any */ if(count > 0) { sum += * (unsigned char *) addr; } /* Fold 32-bit sum to 16 bits */ while (sum>>16) { sum = (sum & 0xffff) + (sum >> 16); } return(~sum); }