void throttler_start(struct Throttler *throttler, double max_rate) { unsigned i; memset(throttler, 0, sizeof(*throttler)); throttler->max_rate = max_rate; for (i=0; i<sizeof(throttler->buckets)/sizeof(throttler->buckets[0]); i++) { throttler->buckets[i].timestamp = pixie_gettime(); throttler->buckets[i].packet_count = 0; } throttler->batch_size = 1; LOG(1, "maxrate = %0.2f\n", throttler->max_rate); }
/*************************************************************************** * Print a status message about once-per-second to the command-line. This * algorithm is a little funky because checking the timestamp on EVERY * packet is slow. ***************************************************************************/ void status_print( struct Status *status, uint64_t count, uint64_t max_count, double x) { double elapsed_time; double rate; double now; double percent_done; double time_remaining; /* * #### FUGGLY TIME HACK #### * * PF_RING doesn't timestamp packets well, so we can't base time from * incoming packets. Checking the time ourself is too ugly on per-packet * basis. Therefore, we are going to create a global variable that keeps * the time, and update that variable whenever it's convienient. This * is one of those convenient places. */ global_now = time(0); /* Get the time. NOTE: this is CLOCK_MONOTONIC_RAW on Linux, not * wall-clock time. */ now = (double)pixie_gettime(); /* Figure how many SECONDS have elapsed, in a floating point value. * Since the above timestamp is in microseconds, we need to * shift it by 1-million */ elapsed_time = (now - status->last.clock)/1000000.0; if (elapsed_time == 0) return; /* Figure out the "packets-per-second" number, which is just: * * rate = packets_sent / elapsed_time; */ rate = (count - status->last.count)*1.0/elapsed_time; /* * Smooth the number by averaging over the last 8 seconds */ status->last_rates[status->last_count++ & 0x7] = rate; rate = status->last_rates[0] + status->last_rates[1] + status->last_rates[2] + status->last_rates[3] + status->last_rates[4] + status->last_rates[5] + status->last_rates[6] + status->last_rates[7] ; rate /= 8; if (rate == 0) return; /* * Calculate "percent-done", which is just the total number of * packets sent divided by the number we need to send. */ percent_done = (double)(count*100.0/max_count); /* * Calulate the time remaining in the scan */ time_remaining = (1.0 - percent_done/100.0) * (max_count / rate); /* * Print the message to <stderr> so that <stdout> can be redirected * to a file (<stdout> reports what systems were found). */ fprintf(stderr, "rate:%6.2f-kpps, %5.2f%% done,%4u:%02u:%02u remaining, %llu-tcbs, \r", x/1000.0, percent_done, (unsigned)(time_remaining/60/60), (unsigned)(time_remaining/60)%60, (unsigned)(time_remaining)%60, global_tcb_count //(unsigned)rate ); fflush(stderr); /* * Remember the values to be diffed against the next time around */ status->last.clock = now; status->last.count = count; }
struct Adapter * rawsock_init_adapter(const char *adapter_name, unsigned is_pfring, unsigned is_sendq, unsigned is_packet_trace, unsigned is_offline, const char *bpf_filter, unsigned is_vlan, unsigned vlan_id) { struct Adapter *adapter; char errbuf[PCAP_ERRBUF_SIZE]; adapter = (struct Adapter *)malloc(sizeof(*adapter)); if (adapter == NULL) exit(1); memset(adapter, 0, sizeof(*adapter)); adapter->is_packet_trace = is_packet_trace; adapter->pt_start = 1.0 * pixie_gettime() / 1000000.0; adapter->is_vlan = is_vlan; adapter->vlan_id = vlan_id; if (is_offline) return adapter; /*---------------------------------------------------------------- * PORTABILITY: WINDOWS * If is all digits index, then look in indexed list *----------------------------------------------------------------*/ if (is_numeric_index(adapter_name)) { const char *new_adapter_name; new_adapter_name = adapter_from_index(atoi(adapter_name)); if (new_adapter_name == 0) { fprintf(stderr, "pcap_open_live(%s) error: bad index\n", adapter_name); return 0; } else adapter_name = new_adapter_name; } /*---------------------------------------------------------------- * PORTABILITY: PF_RING * If we've been told to use --pfring, then attempt to open the * network adapter usign the PF_RING API rather than libpcap. * Since a lot of things can go wrong, we do a lot of extra * logging here. *----------------------------------------------------------------*/ if (is_pfring || is_pfring_dna(adapter_name)) { int err; unsigned version; /* * Open * * TODO: Do we need the PF_RING_REENTRANT flag? We only have one * transmit and one receive thread, so I don't think we need it. * Also, this reduces performance in half, from 12-mpps to * 6-mpps. * NOTE: I don't think it needs the "re-entrant" flag, because it * transmit and receive are separate functions? */ LOG(2, "pfring:'%s': opening...\n", adapter_name); adapter->ring = PFRING.open(adapter_name, 1500, 0);//PF_RING_REENTRANT); adapter->pcap = (pcap_t*)adapter->ring; adapter->link_type = 1; if (adapter->ring == NULL) { LOG(0, "pfring:'%s': OPEN ERROR: %s\n", adapter_name, strerror_x(errno)); return 0; } else LOG(1, "pfring:'%s': successfully opened\n", adapter_name); /* * Housekeeping */ PFRING.set_application_name(adapter->ring, "masscan"); PFRING.version(adapter->ring, &version); LOG(1, "pfring: version %d.%d.%d\n", (version >> 16) & 0xFFFF, (version >> 8) & 0xFF, (version >> 0) & 0xFF); LOG(2, "pfring:'%s': setting direction\n", adapter_name); err = PFRING.set_direction(adapter->ring, rx_only_direction); if (err) { fprintf(stderr, "pfring:'%s': setdirection = %d\n", adapter_name, err); } else LOG(2, "pfring:'%s': direction success\n", adapter_name); /* * Activate * * PF_RING requires a separate activation step. */ LOG(2, "pfring:'%s': activating\n", adapter_name); err = PFRING.enable_ring(adapter->ring); if (err != 0) { LOG(0, "pfring: '%s': ENABLE ERROR: %s\n", adapter_name, strerror_x(errno)); PFRING.close(adapter->ring); adapter->ring = 0; return 0; } else LOG(1, "pfring:'%s': successfully enabled\n", adapter_name); return adapter; }
/*************************************************************************** * We return the number of packets that can be sent in a batch. Thus, * instead of trying to throttle each packet individually, which has a * high per-packet cost, we try to throttle a bunch at a time. Normally, * this function will return 1, only at high rates does it return larger * numbers. * * NOTE: The minimum value this returns is 1. When it's less than that, * it'll pause and wait until it's ready to send a packet. ***************************************************************************/ uint64_t throttler_next_batch(struct Throttler *throttler, uint64_t packet_count) { uint64_t timestamp; uint64_t index; uint64_t old_timestamp; uint64_t old_packet_count; double current_rate; double max_rate = throttler->max_rate; again: /* NOTE: this uses CLOCK_MONOTONIC_RAW on Linux, so the timstamp doesn't * move forward when the machine is suspended */ timestamp = pixie_gettime(); /* * We record that last 256 buckets, and average the rate over all of * them. */ index = (throttler->index) & 0xFF; throttler->buckets[index].timestamp = timestamp; throttler->buckets[index].packet_count = packet_count; index = (++throttler->index) & 0xFF; old_timestamp = throttler->buckets[index].timestamp; old_packet_count = throttler->buckets[index].packet_count; /* * If the delay is more than 1-second, then we should reset the system * in order to avoid transmittting too fast. */ if (timestamp - old_timestamp > 1000000) { //throttler_start(throttler, throttler->max_rate); throttler->batch_size = 1; goto again; } /* * Calculate the recent rate. * NOTE: this isn't the rate "since start", but only the "recent" rate. * That's so that if the system pauses for a while, we don't flood the * network trying to catch up. */ current_rate = 1.0*(packet_count - old_packet_count)/((timestamp - old_timestamp)/1000000.0); /* * If we've been going too fast, then <pause> for a moment, then * try again. */ if (current_rate > max_rate) { double waittime; /* calculate waittime, in seconds */ waittime = (current_rate - max_rate) / throttler->max_rate; /* At higher rates of speed, we don't actually need to wait the full * interval. It's better to have a much smaller interval, so that * we converge back on the true rate faster */ waittime *= 0.1; /* This is in case of gross failure of the system. This should never * actually happen, unless there is a bug. Really, I ought to make * this an 'assert()' instead to fail and fix the bug rather than * silently continueing, but I'm too lazy */ if (waittime > 0.1) waittime = 0.1; /* Since we've exceeded the speed limit, we should reduce the * batch size slightly. We don't do it only by a little bit to * avoid over-correcting. We want to converge on the correct * speed gradually. Note that since this happens hundres or * thousands of times a second, the convergence is very fast * even with 0.1% adjustment */ throttler->batch_size *= 0.999; /* Now we wait for a bit */ pixie_usleep((uint64_t)(waittime * 1000000.0)); /* There are two choices here. We could either return immediately, * or we can loop around again. Right now, the code loops around * again in order to support very slow rates, such as 0.5 packets * per second. Nobody would want to run a scanner that slowly of * course, but it's great for testing */ //return (uint64_t)throttler->batch_size; goto again; } /* * Calculate how many packets are needed to catch up again to the current * rate, and return that. * * NOTE: this is almost always going to have the value of 1 (one). Only at * very high speeds (above 100,000 packets/second) will this value get * larger. */ throttler->batch_size *= 1.005; if (throttler->batch_size > 10000) throttler->batch_size = 10000; throttler->current_rate = current_rate; throttler->test_timestamp = timestamp; throttler->test_packet_count = packet_count; return (uint64_t)throttler->batch_size; }
/*************************************************************************** * Print packet info, when using nmap-style --packet-trace option ***************************************************************************/ void packet_trace(FILE *fp, const unsigned char *px, size_t length, unsigned is_sent) { unsigned x; struct PreprocessedInfo parsed; unsigned src_ip; unsigned dst_ip; char from[32]; char to[32]; char sz_type[32]; unsigned type; double timestamp = 1.0 * pixie_gettime() / 1000000.0; unsigned offset; const char *direction; if (is_sent) direction = "SENT"; else direction = "RCVD"; /* parse the packet */ x = preprocess_frame(px, (unsigned)length, 1, &parsed); if (!x) return; offset = parsed.found_offset; src_ip = parsed.ip_src[0] << 24 | parsed.ip_src[1] << 16 | parsed.ip_src[2] << 8 | parsed.ip_src[3]; dst_ip = parsed.ip_dst[0] << 24 | parsed.ip_dst[1] << 16 | parsed.ip_dst[2] << 8 | parsed.ip_dst[3]; /* format the IP addresses into fixed-width fields */ sprintf_s(from, sizeof(from), "%u.%u.%u.%u:%u", (src_ip>>24)&0xFF, (src_ip>>16)&0xFF, (src_ip>>8)&0xFF, (src_ip>>0)&0xFF, parsed.port_src); sprintf_s(to, sizeof(to), "%u.%u.%u.%u:%u", (dst_ip>>24)&0xFF, (dst_ip>>16)&0xFF, (dst_ip>>8)&0xFF, (dst_ip>>0)&0xFF, parsed.port_dst); switch (parsed.found) { case FOUND_ARP: type = px[offset+6]<<8 | px[offset+7]; *strchr(to, ':') = '\0'; *strchr(from, ':') = '\0'; switch (type) { case 1:strcpy_s(sz_type, sizeof(sz_type), "request"); break; case 2:strcpy_s(sz_type, sizeof(sz_type), "response"); break; default: sprintf_s(sz_type, sizeof(sz_type), "unknown(%u)", type); break; } fprintf(fp, "%s (%5.4f) ARP %-21s > %-21s %s\n", direction, timestamp - global_timestamp_start, from, to, sz_type); break; case FOUND_DNS: case FOUND_UDP: fprintf(fp, "%s (%5.4f) UDP %-21s > %-21s \n", direction, timestamp - global_timestamp_start, from, to); break; case FOUND_ICMP: fprintf(fp, "%s (%5.4f) ICMP %-21s > %-21s \n", direction, timestamp - global_timestamp_start, from, to); break; case FOUND_TCP: type = px[offset+13]; switch (type) { case 0x00: strcpy_s(sz_type, sizeof(sz_type), "NULL"); break; case 0x01: strcpy_s(sz_type, sizeof(sz_type), "FIN"); break; case 0x11: strcpy_s(sz_type, sizeof(sz_type), "FIN-ACK"); break; case 0x19: strcpy_s(sz_type, sizeof(sz_type), "FIN-ACK-PSH"); break; case 0x02: strcpy_s(sz_type, sizeof(sz_type), "SYN"); break; case 0x12: strcpy_s(sz_type, sizeof(sz_type), "SYN-ACK"); break; case 0x04: strcpy_s(sz_type, sizeof(sz_type), "RST"); break; case 0x14: strcpy_s(sz_type, sizeof(sz_type), "RST-ACK"); break; case 0x15: strcpy_s(sz_type, sizeof(sz_type), "RST-FIN-ACK"); break; case 0x10: strcpy_s(sz_type, sizeof(sz_type), "ACK"); break; case 0x18: strcpy_s(sz_type, sizeof(sz_type), "ACK-PSH"); break; default: sprintf_s(sz_type, sizeof(sz_type), "%s%s%s%s%s%s%s%s", (type&0x01)?"FIN":"", (type&0x02)?"SYN":"", (type&0x04)?"RST":"", (type&0x08)?"PSH":"", (type&0x10)?"ACK":"", (type&0x20)?"URG":"", (type&0x40)?"ECE":"", (type&0x80)?"CWR":"" ); break; } fprintf(fp, "%s (%5.4f) TCP %-21s > %-21s %s\n", direction, timestamp - global_timestamp_start, from, to, sz_type); break; case FOUND_IPV6: break; default: fprintf(fp, "%s (%5.4f) UNK %-21s > %-21s [%u]\n", direction, timestamp - global_timestamp_start, from, to, parsed.found); break; } }
int perftest(int argc, char *argv[]) { struct PerfTest perftest[1]; struct ZoneFileParser *parser; struct Catalog *db; size_t i; perftest->loop_count = 10000000; /* * Create a pseudo-network subsystem for generating packets */ perftest->server.parent = perftest; perftest->server.adapter = adapter_create( perftest_alloc_packet, perftest_server_to_client_response, &perftest->server); adapter_add_ipv4(perftest->server.adapter, 0xC0A80101, 0xFFFFffff); /* create a catalog/database, this is where all the parsed zonefile * records will be put */ perftest->db = catalog_create(); perftest->thread->catalog = perftest->db; db = perftest->db; /* * Parse a sample zone */ parser = zonefile_begin( example_origin, /* origin */ 60, /* TTL */ 10000, /* filesize */ "<perftest>", /* filename */ zonefile_load, /* callback */ db, /* callback data */ 0 ); zonefile_set_singlestep(parser); for (i=0; perftest_zone[i]; i++) { zonefile_parse(parser, (const unsigned char*)perftest_zone[i], strlen(perftest_zone[i]) ); } zonefile_end(parser); /* * Send packets. This creates one thread per CPU processing requests. */ { unsigned threads_desired = pixie_cpu_get_count(); uint64_t start, stop; double requests_per_second; fprintf(stderr, "running %u threads\n", threads_desired); start = pixie_gettime(); for (i=0; i<threads_desired; i++) { __sync_fetch_and_add(&threads_running, 1); pixie_begin_thread((void(*)(void*))run_perf, 0, perftest); } while (threads_running) pixie_usleep(1000); stop = pixie_gettime(); requests_per_second = 1000000.0 * (1.0 * threads_desired * perftest->loop_count) / (stop - start); fprintf(stderr, "queries = %u\n", (unsigned)(threads_desired * perftest->loop_count)); fprintf(stderr, "seconds = %5.3f\n", (stop - start)/1000000.0); fprintf(stderr, "queries/second = %5.3f\n", requests_per_second); } exit(1); return 0; }