/* Pass the Parcel */ /* Multihost Network + TCP/IP Stack Overloading Tool */ /* Version 0.01b */ /* Carl Ritson - 2001 */ #include #include #include #include #include #include #include #include #include #include #include /* * XXX: Excessive comments inline, note to self, * don't spend a whole hour reading over the code once finished... */ /* FIXME: This is just a number I pulled from thin air :/ */ #define MCAST_GROUP "224.7.7.7" enum { PORT = 6505, PKTSIZE = 1400, /* XXX: Warning DEBUG outputs about 3-5 messages per packet */ DEBUG = 0, /* * The number of packet to receive and forward before regenerating * the random packet and adding extra packet to the network */ MAXPKT = 5000, /* * Its a good idea to have the ZONESIZE set to around the * number of hosts you have of the same type. * e.g. if you have say 10 100Mbps Machines and 5 10Mbps Machines * set ZONESIZE to 10. */ ZONESIZE = 4, /* * If you have lots of hosts with many different speeds you may want to * increase the number of ZONES */ ZONES = 3, /* How frequenty hosts are balanced between the ZONES */ BALANCE_INTERVAL = 15, MAX_BALANCES = 10, /* XXX: Only used when DEBUG > 0 */ /* * You may want to change this to reflect the average link speed * in octets.. */ MAX_OCTETS_SEC = 12*1024*1024, /* * The number of seconds to sleep before actualy starting */ PRESLEEP = 10 }; typedef unsigned long hash_t; /* * This is the main host structure, * holds statistics and address structure, * serves as a hash bucket and a linked list item */ struct host_t { hash_t hash; struct sockaddr_in addr; struct host_t *hnext; /* Next Hash Bucket */ struct host_t *lnext; /* Next List Item */ unsigned long sent; unsigned long recv; unsigned long in_octets; unsigned long out_octets; int zone; int badness; }; /* Global Frames Counter */ int frames = 0; /* Head of The Linked List */ struct host_t *host_list = NULL; /* ZONES Hash Tables */ struct host_t *zone[ZONES][ZONESIZE]; /* Hosts currently in a particular zone */ int hosts_in[ZONES]; int *zmap; /* Used for Random Host Selection */ int total_hosts = 0; /* Packets Allocated , to zones and globaly */ unsigned long allocs_remaining = 0; unsigned long allocations[ZONES]; /* Else we spend all day calculating 2 to the the power ZONES */ int tpz; /* Random Packet and its currently used size */ char packet[PKTSIZE]; int pktsize = PKTSIZE; /* Comms Socket */ int lsocket = 0; /* BEGIN FUNCTIONS */ /* hash, Hashing Function, acts on an IP Address */ hash_t hash(struct in_addr *in) { int i; char *p = (char *)in; hash_t ha; /* Magic Number: 4 Bytes in an IP Address */ for(i = 0; i < 4; ++i) { ha = ha * 37 + (int)*(p+i); } return (ha % ZONESIZE); } /* power, returns in to the power pow */ double power(double in, unsigned int pow) { double out = in; int i; for(i = 1; i < pow; ++i) { out *= in; } return out; } /* halloc, mallocs and initializes a host structure */ struct host_t *halloc(char *ip, struct in_addr *in) { struct host_t *p = malloc(sizeof(struct host_t)); if(p != NULL) { p->hnext = NULL; p->lnext = NULL; p->sent = 0; p->recv = 0; p->in_octets = 0; p->out_octets = 0; p->badness = 0; p->addr.sin_family = AF_INET; p->addr.sin_port = htons(PORT); if(ip == NULL && in != NULL) { memcpy(&p->addr.sin_addr,in,sizeof(struct in_addr)); } else { inet_aton(ip,&p->addr.sin_addr); } } return p; } /* place_host, places host h, in zone z */ void place_host(struct host_t *h, int z) { struct host_t *p; if(zone[z][h->hash] == NULL) { zone[z][h->hash] = h; } else { p = zone[z][h->hash]; while(p->hnext != NULL) { p = p->hnext; } p->hnext = h; } h->zone = z; ++hosts_in[z]; h->hnext = NULL; } /* adrcmp, compares two ip addresses, returns 1 on match */ int adrcmp(struct in_addr *a, struct in_addr *b) { if(memcmp(a,b,sizeof(struct in_addr)) == 0) { return 1; } else { return 0; } } /* lfind_host, find a host structure using the host linked list */ /* XXX: I don't think this is used anywhere */ struct host_t *lfind_host(char *cip, struct in_addr *in) { struct in_addr ip; struct host_t *p = host_list; if(cip == NULL && in != NULL) { memcpy(&ip,in,sizeof(struct in_addr)); } else { inet_aton(cip,&ip); } while(p != NULL) { if(adrcmp(&p->addr.sin_addr,&ip)) { return p; } else { p = p->lnext; } } if(p == NULL && DEBUG) {fprintf(stderr,"lfind_host, return: %p\n",p);} return p; } /* * find_host, * finds a host by ip address which is taken in string form * or network byte form (struct in_addr). */ struct host_t *find_host(char *cip, struct in_addr *in) { struct in_addr ip; struct host_t *p = NULL; struct host_t *h = NULL; hash_t ha; int z; if(cip == NULL && in != NULL) { memcpy(&ip,in,sizeof(struct in_addr)); } else { inet_aton(cip,&ip); } ha = hash(&ip); /* * This is the only possible slow down, if we have lots of ZONES * we have to check them all */ for(z = 0; z < ZONES; ++z) { p = zone[z][ha]; if(p != NULL) { if(adrcmp(&p->addr.sin_addr,&ip)) { h = p; } else if(p->hnext != NULL) { p = p->hnext; while(p != NULL) { if(adrcmp(&p->addr.sin_addr,&ip)) { h = p; } p = p->hnext; } } } if(h != NULL) { break; } } if(h == NULL && DEBUG) {fprintf(stderr,"find_host, return: %p\n",h);} return h; } /* * add_host, * adds a host to the hosts list, as find_host takes char or long ip */ struct host_t *add_host(char *ip, struct in_addr *in) { struct host_t *p = host_list; struct host_t *h = NULL; static int z = 0; /* XXX: this is static for a reason */ hash_t ha; if(DEBUG) {fprintf(stderr,"add_host, ip %s\n",ip);} /* Messy Exceptions */ if(in != NULL) { if(DEBUG) {fprintf(stderr,"add_host, in '%s'\n",inet_ntoa(*in));} if(in->s_addr == INADDR_ANY || in->s_addr == INADDR_LOOPBACK) { return NULL; } } else { if(DEBUG) {fprintf(stderr,"add_host, ip '%s'\n",ip);} if(strlen(ip) < 4) { return NULL; } } /* Got pass expections, so we must be adding the host */ if(p == NULL) { p = halloc(ip,in); host_list = p; if(DEBUG) {fprintf(stderr,"add_host, set host_list: %p\n",p);} } else { while(p->lnext != NULL) { p = p->lnext; } p->lnext = halloc(ip,in); p = p->lnext; } if(p == NULL) { fprintf(stderr,"Malloc Failed\n"); exit(1); } else { /* * Using the static z, we can spread additions accross calls. * We don't place any hosts in the last zone, as it is ment * for bad hosts. */ if(hosts_in[z] >= ZONESIZE) { ++z; /* Rotate Allocations */ if(z >= (ZONES - 1)) { z = 0; } } ha = hash(&p->addr.sin_addr); if(zone[z][ha] == NULL) { zone[z][ha] = p; } else { h = zone[z][ha]; while(h->hnext != NULL) { h = h->hnext; } h->hnext = p; } p->hash = ha; p->zone = z; ++hosts_in[z]; ++total_hosts; } if(DEBUG) {fprintf(stderr,"add_host, return: %p\n",p);} return p; } /* * rgen, * random number generator using random. * 0 =< x < high * where x is the number generated. */ int rgen(int high) { return (int) (((double)high)*rand()/(RAND_MAX+1.0)); } /* pick_zone, returns the 'best' zone */ int pick_zone() { static int last_zone = ZONES - 1; /* More Statics */ int z; /* * We still have packets to allocate we pick the next zone * with packets left to allocate, however we use the static * last_zone to jitter allocations, to make it more random */ if(allocs_remaining > 0) { for(z = 0; z < ZONES; ++z) { if(allocations[z] > 0 && z != last_zone && hosts_in[z] > 0) { last_zone = z; --allocs_remaining; --allocations[z]; return z; } } } /* * allocations have now run out or failed for some reason * we randomly pick a zone using the zone_map, which favours * the lower zones... */ z = zmap[rgen(tpz)]; while(hosts_in[z] < 1) { z = zmap[rgen(tpz)]; } return z; } /* rand_host, returns a 'random' host, not quite as it uses the 'best' zone */ struct host_t *rand_host() { struct host_t *p = NULL; int z; /* If there are no hosts to pick from why bother going any further */ if(total_hosts == 0) { if(DEBUG) {fprintf(stderr,"rand_host: There are no hosts!\n");} return NULL; } z = pick_zone(); if(DEBUG) {fprintf(stderr,"rand_host, z: %d\n",z);} /* * This is the main failing if a zone is quite empty we will spend * alot of time in here spinning till we find a host. * Thats why its a good idea to keep the ZONESIZE small */ while(p == NULL) { p = zone[z][rgen(ZONESIZE)]; } /* */ if(p != NULL) { struct host_t *h = p; int i = 0; /* Count the number of hosts in the chain */ while(h != NULL) { ++i; h = h->hnext; } /* Pick one at random */ z = rgen(i); for(i = 0; i < z; ++i) { p = p->hnext; } } if(DEBUG) {fprintf(stderr,"rand_host, return: %p\n",p);} return p; } /* add_ips, add ip addresses of hosts from file pointer in */ void add_ips(FILE *in) { char lb[1024]; char *p = lb; int c = fgetc(in); while(c != EOF) { while(c != EOF && c != '\n' && ((p - 1024) < lb)) { *(p++) = (char) c; c = fgetc(in); } *p = '\0'; add_host(lb,NULL); p = lb; c = fgetc(in); } } /* * gen_packet, * regenerate the random packet with random data and random length */ void gen_packet() { int i; pktsize = rgen(PKTSIZE); for(i = 0; i < pktsize; ++i) { packet[i] = (char) rgen(255); } } /* forward, forward the packet pointed to by buffer to a 'random' host */ void forward(char *buffer, int size) { struct host_t *h = rand_host(); if(DEBUG) {fprintf(stderr,"Forward to: %p, size: %d\n",h,size);} if(h != NULL) { h->out_octets += size; ++h->sent; sendto(lsocket, buffer, size, 0, (struct sockaddr *)&(h->addr), sizeof(struct sockaddr_in)); } } /* send_rand, sends count random packets */ void send_rand(int count) { int i; for(i = 0; i < count; ++i) { forward(packet, pktsize); } } /* init_zones, clears the zones, e.g. all pointers = NULL */ void init_zones() { int i,j; for(i = 0; i < ZONES; ++i) { hosts_in[i] = 0; for(j = 0; j < ZONESIZE; ++j) { zone[i][j] = NULL; } } } /* report, reports the state of the zones */ void report() { struct host_t *h = host_list; int z; fprintf(stderr,"Total Hosts: %d\n",total_hosts); for (z = 0; z < ZONES; ++z) { fprintf(stderr,"%d Hosts in Zone %d\n",hosts_in[z],z); } while(h != NULL) { fprintf(stderr,"Host: %s, zone: %d, badness: %d\n",inet_ntoa(h->addr.sin_addr),h->zone,h->badness); h = h->lnext; } } /* reset_counters, resets the statistical counters of the host h */ void reset_counters(struct host_t *h) { h->recv = 0; h->sent = 0; h->in_octets = 0; h->out_octets = 0; } /* hdetails, outputs the statistical counters of host h */ /* XXX: only used when DEBUG > 0 */ void hdetails(struct host_t *h) { fprintf(stderr,"host: %s, recv: %d, sent: %d, in_octets: %d, out_octets: %d\n",inet_ntoa(h->addr.sin_addr),h->recv,h->sent,h->in_octets,h->out_octets); } /* set_allocations, allocate packets to the zones */ void set_allocations(unsigned long packets, unsigned long octets) { int z; /* If we are below half of the max link speed add MAX_PKT packets */ if((octets/BALANCE_INTERVAL) < (MAX_OCTETS_SEC/2)) { packets += MAXPKT; } allocs_remaining = packets; for(z = (ZONES - 1); z >= 0; --z) { /* Only allocate packets to a zone if it contains hosts */ if(hosts_in[z] > 0) { allocations[z] = packets / (z+1); packets -= packets / (z+1); } } } /* balance_zones, balances the hosts between the zones and print stats */ void balance_zones() { /* Not too bad we walk the hosts list, ZONES+2 times */ struct host_t *l = NULL; struct host_t *h = host_list; unsigned long highest = 0; long tsent = 0; long trecv = 0; long osent = 0; long orecv = 0; double in_rate; double out_rate; int z; /* * First Walk do the Following: * 1. find highest recv count. * 2. ++badness if no packet were received, * if badness > 1, host is removed, * place any hosts with badness in last zone. * 3. Take the statistics from the hosts. * 4. Set the zone of any none bad hosts to -1 */ while(h != NULL) { /* Get stats */ orecv += h->in_octets; osent += h->out_octets; trecv += h->recv; tsent += h->sent; if(DEBUG) {hdetails(h);} /* Process info */ if(h->recv > highest) { highest = h->recv; h->zone = -1; l = h; h = h->lnext; } else if(h->recv == 0) { ++h->badness; if(h->badness > 1) { if(DEBUG) {fprintf(stderr,"Removing %s\n",inet_ntoa(h->addr.sin_addr));} if(l == NULL) { host_list = h->lnext; free(h); h = host_list; } else { l->lnext = h->lnext; free(h); h = l->lnext; } --total_hosts; } else { place_host(h,(ZONES - 1)); l = h; h = h->lnext; } } else { h->zone = -1; l = h; h = h->lnext; } } if(DEBUG) {fprintf(stderr,"balance_zones, highest: %d\n",highest);} /* Clear The Zones */ init_zones(); /* Fill all zones but the one before last */ for(z = 0; z < (ZONES - 2); ++z) { h = host_list; highest = (unsigned long)((double)highest / 2.0); while(h != NULL) { if(h->recv > highest && h->zone == -1) { place_host(h,z); h->badness = 0; /* Remove Badness */ } h = h->lnext; } } /* * Fill the zone before last, * while we are at it reset the counters. * This ZONE before the last thing is done so that the badness on * hosts sending low numbers of packet is maintained, so a host * that may have stopped sending packets is quickly evicted. */ z = (ZONES - 2); h = host_list; while(h != NULL) { if(h->recv <= highest && h->zone == -1) { place_host(h,z); } reset_counters(h); h = h->lnext; } /* * Set Allocations for Zones, allocate from which ever is greater of * total received packets or MAXPKT. */ set_allocations((trecv < MAXPKT ? MAXPKT : trecv), orecv); /* Calculate and Print Statistics */ out_rate = (((double) osent)/((double) BALANCE_INTERVAL)) / 1024.0; in_rate = (((double) orecv)/((double) BALANCE_INTERVAL)) / 1024.0; fprintf(stderr,"Rate in: %.0fkb, Rate out: %.0fkb\n", in_rate, out_rate); fprintf(stderr,"Packets in: %d, Packets out: %d\n", trecv, tsent); } /* mcast, send a multicast announcement of ourselves */ void mcast() { static struct sockaddr_in *addr = NULL; int r; if(addr == NULL) { addr = malloc(sizeof(struct sockaddr_in)); addr->sin_family = AF_INET; addr->sin_port = htons(PORT); inet_aton(MCAST_GROUP,&(addr->sin_addr)); } r = sendto(lsocket, packet, pktsize, 0, (struct sockaddr *)addr, sizeof(struct sockaddr_in)); if(DEBUG) {fprintf(stderr,"mcast, r: %d\n",r);} } /* Main Balancing+Reporting Loop runs every BALANCE_INTERVAL seconds */ void alarm_handler(int sig) { static int calls = 0; /* Balance the zones */ /* this also reports bandwidth, etc */ balance_zones(); /* Report the state of the zones */ report(); /* Regenerate the random packet */ gen_packet(); /* Fire off some extra packets */ send_rand(5); /* Reannounce ourself */ /* The multicast packets also feed the loop */ mcast(); /* Reprogram the alarm timer */ alarm(BALANCE_INTERVAL); /* if debugging we leave after MAX_BALANCES */ if(DEBUG) { ++calls; if(calls > MAX_BALANCES) { exit(0); } } } void init_zmap() { double t; int i = 0,j = 0,k = 1; /* Program 2 to the power ZONES value */ tpz = power(2, ZONES); /* zmap is treated as an array but its size is only now know */ /* so we malloc space for it */ zmap = malloc(sizeof(int) * tpz); if(zmap == NULL) { fprintf(stderr,"zmap: Malloc Failed\n"); exit(1); } zmap[0] = 0; /* Zone 0 always has 1 extra mapping */ for(i = 0; i < ZONES; ++i) { allocations[i] = 0; t = tpz * (power(0.5,(i+1))); for(j = 0; j < t; ++j) { zmap[j+k] = i; } k += (int) t; } if(DEBUG) { for(i = 0; i < tpz; ++i) { fprintf(stderr,"zmap[%d] = %d\n",i,zmap[i]); } } } int main(int argc, char *argv[]) { char buffer[PKTSIZE]; int size = 0; socklen_t len = 0; struct host_t *h; FILE *input; struct sockaddr_in addr; struct ip_mreqn mcast_addr; int r; const int zero = 0; /* Program Listenning Address */ addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = INADDR_ANY; /* Clear Zones */ init_zones(); /* Initalize the Zone Mappings */ init_zmap(); /* A few Debug checks */ if(DEBUG) {fprintf(stderr,"2^2 == %d\n",(int)power(2,2));} if(DEBUG) {fprintf(stderr,"0.5^2 == %f\n",power(0.5,2));} if(DEBUG) {fprintf(stderr,"tpz == %d\n",tpz);} /* Generate the first random packet */ gen_packet(); /* Check Arguments and Load IP List if there is one */ if(argc >= 2) { input = fopen(argv[1],"r"); add_ips(input); fclose(input); } /* Setup UDP Socket */ lsocket = socket(PF_INET, SOCK_DGRAM, 0); bind(lsocket, (struct sockaddr *)&addr, sizeof(addr)); /* Join it to the Appropriate Multicast Group */ inet_aton(MCAST_GROUP,&(mcast_addr.imr_multiaddr)); mcast_addr.imr_address.s_addr = INADDR_ANY; mcast_addr.imr_ifindex = 0; r = setsockopt(lsocket, 0, IP_ADD_MEMBERSHIP, &mcast_addr, sizeof(struct ip_mreqn)); if(r == -1) { fprintf(stderr,"Error: %s\n",sys_errlist[errno]); exit(1); } r = setsockopt(lsocket, 0, IP_MULTICAST_LOOP, &zero, sizeof(int)); if(r == -1) { fprintf(stderr,"Error: %s\n",sys_errlist[errno]); exit(1); } /* Setup Alarm Handler */ signal(SIGALRM,&(alarm_handler)); /* Report ZONE states */ report(); /* Sleep For PRESLEEP seconds, to make sure everything is ready */ sleep(PRESLEEP); if(DEBUG) {fprintf(stderr,"Let the fun begin\n");} /* Program the alarm, this sets the balancing rolling */ alarm(BALANCE_INTERVAL); /* Announce ourselves */ mcast(); /* Fire off some Packets */ send_rand(5); /* This is the Main Loop */ for(;;) { /* Receive a Packet */ size = recvfrom(lsocket, buffer, PKTSIZE, 0, (struct sockaddr *)&addr, &len); ++frames; if(DEBUG) {fprintf(stderr,"Got Frame, Cycle:%d\n",frames);} /* Get the h structure for the just received frame */ h = find_host(NULL,&addr.sin_addr); if(h == NULL) { /* If the host doesn't exist add it */ h = add_host(NULL,&addr.sin_addr); } /* Add_host can fail if the host is bogus e.g. 0.0.0.0 */ if(h != NULL) { h->recv++; h->in_octets += size; forward(buffer, size); /* Every MAXPKT frames the random packet is regenerated */ /* and another extra packet is sent */ if(frames >= MAXPKT) { gen_packet(); send_rand(1); frames = 0; } } else { if(DEBUG) {fprintf(stderr,"main, h == (NULL)\n");} } } return 0; }