484 lines
14 KiB
C
484 lines
14 KiB
C
/*
|
|
* ZMap Copyright 2013 Regents of the University of Michigan
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
* use this file except in compliance with the License. You may obtain a copy
|
|
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include <assert.h>
|
|
#include <sched.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <net/if.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include <pcap/pcap.h>
|
|
#include <pthread.h>
|
|
|
|
#include "../lib/logger.h"
|
|
#include "../lib/random.h"
|
|
|
|
#include "zopt.h"
|
|
#include "send.h"
|
|
#include "recv.h"
|
|
#include "state.h"
|
|
#include "monitor.h"
|
|
#include "output_modules/output_modules.h"
|
|
#include "probe_modules/probe_modules.h"
|
|
#include "get_gateway.h"
|
|
|
|
pthread_mutex_t cpu_affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
pthread_mutex_t recv_ready_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
static void set_cpu(void)
|
|
{
|
|
pthread_mutex_lock(&cpu_affinity_mutex);
|
|
static int core=0;
|
|
int num_cores = sysconf(_SC_NPROCESSORS_ONLN);
|
|
cpu_set_t cpuset;
|
|
CPU_ZERO(&cpuset);
|
|
CPU_SET(core, &cpuset);
|
|
if (pthread_setaffinity_np(pthread_self(),
|
|
sizeof(cpu_set_t), &cpuset) != 0) {
|
|
log_error("zmap", "can't set thread CPU affinity");
|
|
}
|
|
log_trace("zmap", "set thread %u affinity to core %d",
|
|
pthread_self(), core);
|
|
core = (core + 1) % num_cores;
|
|
pthread_mutex_unlock(&cpu_affinity_mutex);
|
|
}
|
|
|
|
static void* start_send(__attribute__((unused)) void *arg)
|
|
{
|
|
set_cpu();
|
|
send_run();
|
|
return NULL;
|
|
}
|
|
|
|
static void* start_recv(__attribute__((unused)) void *arg)
|
|
{
|
|
set_cpu();
|
|
recv_run(&recv_ready_mutex);
|
|
return NULL;
|
|
}
|
|
|
|
static void *start_mon(__attribute__((unused)) void *arg)
|
|
{
|
|
set_cpu();
|
|
monitor_run();
|
|
return NULL;
|
|
}
|
|
|
|
#define SI(w,x,y) printf("%s\t%s\t%i\n", w, x, y);
|
|
#define SD(w,x,y) printf("%s\t%s\t%f\n", w, x, y);
|
|
#define SU(w,x,y) printf("%s\t%s\t%u\n", w, x, y);
|
|
#define SLU(w,x,y) printf("%s\t%s\t%lu\n", w, x, y);
|
|
#define SS(w,x,y) printf("%s\t%s\t%s\n", w, x, y);
|
|
#define STRTIME_LEN 1024
|
|
|
|
static void summary(void)
|
|
{
|
|
char send_start_time[STRTIME_LEN+1];
|
|
assert(dstrftime(send_start_time, STRTIME_LEN, "%c", zsend.start));
|
|
char send_end_time[STRTIME_LEN+1];
|
|
assert(dstrftime(send_end_time, STRTIME_LEN, "%c", zsend.finish));
|
|
char recv_start_time[STRTIME_LEN+1];
|
|
assert(dstrftime(recv_start_time, STRTIME_LEN, "%c", zrecv.start));
|
|
char recv_end_time[STRTIME_LEN+1];
|
|
assert(dstrftime(recv_end_time, STRTIME_LEN, "%c", zrecv.finish));
|
|
double hitrate = ((double) 100 * zrecv.success_unique)/((double)zsend.sent);
|
|
|
|
SU("cnf", "target-port", zconf.target_port);
|
|
SU("cnf", "source-port-range-begin", zconf.source_port_first);
|
|
SU("cnf", "source-port-range-end", zconf.source_port_last);
|
|
SS("cnf", "source-addr-range-begin", zconf.source_ip_first);
|
|
SS("cnf", "source-addr-range-end", zconf.source_ip_last);
|
|
SU("cnf", "maximum-targets", zconf.max_targets);
|
|
SU("cnf", "maximum-runtime", zconf.max_runtime);
|
|
SU("cnf", "maximum-results", zconf.max_results);
|
|
SU("cnf", "permutation-seed", zconf.seed);
|
|
SI("cnf", "cooldown-period", zconf.cooldown_secs);
|
|
SS("cnf", "send-interface", zconf.iface);
|
|
SI("cnf", "rate", zconf.rate);
|
|
SLU("cnf", "bandwidth", zconf.bandwidth);
|
|
SU("env", "nprocessors", (unsigned) sysconf(_SC_NPROCESSORS_ONLN));
|
|
SS("exc", "send-start-time", send_start_time);
|
|
SS("exc", "send-end-time", send_end_time);
|
|
SS("exc", "recv-start-time", recv_start_time);
|
|
SS("exc", "recv-end-time", recv_end_time);
|
|
SU("exc", "sent", zsend.sent);
|
|
SU("exc", "blacklisted", zsend.blacklisted);
|
|
SU("exc", "first-scanned", zsend.first_scanned);
|
|
SD("exc", "hit-rate", hitrate);
|
|
SU("exc", "success-total", zrecv.success_total);
|
|
SU("exc", "success-unique", zrecv.success_unique);
|
|
SU("exc", "success-cooldown-total", zrecv.cooldown_total);
|
|
SU("exc", "success-cooldown-unique", zrecv.cooldown_unique);
|
|
SU("exc", "failure-total", zrecv.failure_total);
|
|
SU("exc", "sendto-failures", zsend.sendto_failures);
|
|
SU("adv", "permutation-gen", zconf.generator);
|
|
SS("exc", "scan-type", zconf.probe_module->name);
|
|
}
|
|
|
|
static void start_zmap(void)
|
|
{
|
|
log_init(stderr, zconf.log_level);
|
|
log_info("zmap", "started");
|
|
|
|
// finish setting up configuration
|
|
if (zconf.iface == NULL) {
|
|
char errbuf[PCAP_ERRBUF_SIZE];
|
|
char *iface = pcap_lookupdev(errbuf);
|
|
if (iface == NULL) {
|
|
log_fatal("zmap", "could not detect default network interface "
|
|
"(e.g. eth0). Try running as root or setting"
|
|
" interface using -i flag.");
|
|
}
|
|
log_debug("zmap", "no interface provided. will use %s", iface);
|
|
zconf.iface = iface;
|
|
}
|
|
if (zconf.source_ip_first == NULL) {
|
|
struct in_addr default_ip;
|
|
zconf.source_ip_first = malloc(INET_ADDRSTRLEN);
|
|
zconf.source_ip_last = zconf.source_ip_first;
|
|
if (get_iface_ip(zconf.iface, &default_ip) < 0) {
|
|
log_fatal("zmap", "could not detect default IP address for for %s."
|
|
" Try specifying a source address (-S).", zconf.iface);
|
|
}
|
|
inet_ntop(AF_INET, &default_ip, zconf.source_ip_first, INET_ADDRSTRLEN);
|
|
log_debug("zmap", "no source IP address given. will use %s",
|
|
zconf.source_ip_first);
|
|
}
|
|
if (!zconf.gw_mac_set) {
|
|
struct in_addr gw_ip;
|
|
char iface[IF_NAMESIZE];
|
|
if (get_default_gw(&gw_ip, iface) < 0) {
|
|
log_fatal("zmap", "could not detect default gateway address for %i."
|
|
" Try setting default gateway mac address (-G).");
|
|
}
|
|
log_debug("zmap", "found gateway IP %s on %s", inet_ntoa(gw_ip), iface);
|
|
if (get_hw_addr(&gw_ip, iface, zconf.gw_mac) < 0) {
|
|
log_fatal("zmap", "could not detect GW MAC address for %s on %s."
|
|
" Try setting default gateway mac address (-G).",
|
|
inet_ntoa(gw_ip), zconf.iface);
|
|
}
|
|
zconf.gw_mac_set = 1;
|
|
log_debug("zmap", "using default gateway MAC %02x:%02x:%02x:%02x:%02x:%02x",
|
|
zconf.gw_mac[0], zconf.gw_mac[1], zconf.gw_mac[2],
|
|
zconf.gw_mac[3], zconf.gw_mac[4], zconf.gw_mac[5]);
|
|
}
|
|
|
|
// initialization
|
|
if (zconf.output_module && zconf.output_module->init) {
|
|
zconf.output_module->init(&zconf);
|
|
}
|
|
if (send_init()) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (zconf.output_module && zconf.output_module->start) {
|
|
zconf.output_module->start(&zconf, &zsend, &zrecv);
|
|
}
|
|
|
|
// start threads
|
|
pthread_t *tsend, trecv, tmon;
|
|
int r = pthread_create(&trecv, NULL, start_recv, NULL);
|
|
if (r != 0) {
|
|
log_fatal("zmap", "unable to create recv thread");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
for (;;) {
|
|
pthread_mutex_lock(&recv_ready_mutex);
|
|
if (zconf.recv_ready) {
|
|
break;
|
|
}
|
|
pthread_mutex_unlock(&recv_ready_mutex);
|
|
}
|
|
tsend = malloc(zconf.senders * sizeof(pthread_t));
|
|
assert(tsend);
|
|
log_debug("zmap", "using %d sender threads", zconf.senders);
|
|
for (int i=0; i < zconf.senders; i++) {
|
|
r = pthread_create(&tsend[i], NULL, start_send, NULL);
|
|
if (r != 0) {
|
|
log_fatal("zmap", "unable to create send thread");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
if (!zconf.quiet) {
|
|
r = pthread_create(&tmon, NULL, start_mon, NULL);
|
|
if (r != 0) {
|
|
log_fatal("zmap", "unable to create monitor thread");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
// wait for completion
|
|
for (int i=0; i < zconf.senders; i++) {
|
|
pthread_join(tsend[i], NULL);
|
|
if (r != 0) {
|
|
log_fatal("zmap", "unable to join send thread");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
log_debug("zmap", "senders finished");
|
|
pthread_join(trecv, NULL);
|
|
if (r != 0) {
|
|
log_fatal("zmap", "unable to join recv thread");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (!zconf.quiet) {
|
|
pthread_join(tmon, NULL);
|
|
if (r != 0) {
|
|
log_fatal("zmap", "unable to join monitor thread");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
// finished
|
|
if (zconf.summary) {
|
|
summary();
|
|
}
|
|
if (zconf.output_module && zconf.output_module->close) {
|
|
zconf.output_module->close(&zconf, &zsend, &zrecv);
|
|
}
|
|
if (zconf.probe_module && zconf.probe_module->close) {
|
|
zconf.probe_module->close(&zconf, &zsend, &zrecv);
|
|
}
|
|
log_info("zmap", "completed");
|
|
}
|
|
|
|
static void enforce_range(const char *name, int v, int min, int max)
|
|
{
|
|
if (v < min || v > max) {
|
|
fprintf(stderr, "%s: argument `%s' must be between %d and %d\n",
|
|
CMDLINE_PARSER_PACKAGE, name, min, max);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
static int file_exists(char *name)
|
|
{
|
|
FILE *file = fopen(name, "r");
|
|
if (!file)
|
|
return 0;
|
|
fclose(file);
|
|
return 1;
|
|
}
|
|
|
|
#define MAC_LEN IFHWADDRLEN
|
|
int parse_mac(macaddr_t *out, char *in)
|
|
{
|
|
if (strlen(in) < MAC_LEN*3-1)
|
|
return 0;
|
|
char octet[3];
|
|
octet[2] = '\0';
|
|
for (int i=0; i < MAC_LEN; i++) {
|
|
if (i < MAC_LEN-1 && in[i*3+2] != ':') {
|
|
return 0;
|
|
}
|
|
strncpy(octet, &in[i*3], 2);
|
|
char *err = NULL;
|
|
long b = strtol(octet, &err, 16);
|
|
if (err && *err != '\0') {
|
|
return 0;
|
|
}
|
|
out[i] = b & 0xFF;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
#define SET_IF_GIVEN(DST,ARG) \
|
|
{ if (args.ARG##_given) { (DST) = args.ARG##_arg; }; }
|
|
#define SET_BOOL(DST,ARG) \
|
|
{ if (args.ARG##_given) { (DST) = 1; }; }
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct gengetopt_args_info args;
|
|
struct cmdline_parser_params *params;
|
|
params = cmdline_parser_params_create();
|
|
params->initialize = 1;
|
|
params->override = 0;
|
|
params->check_required = 0;
|
|
if (cmdline_parser_ext(argc, argv, &args, params) != 0) {
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
if (args.help_given) {
|
|
cmdline_parser_print_help();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
if (args.version_given) {
|
|
cmdline_parser_print_version();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
if (args.list_output_modules_given) {
|
|
print_output_modules();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
if (args.list_probe_modules_given) {
|
|
print_probe_modules();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
if (args.config_given || file_exists(args.config_arg)) {
|
|
params->initialize = 0;
|
|
params->override = 0;
|
|
if (cmdline_parser_config_file(args.config_arg, &args, params)
|
|
!= 0) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
if (cmdline_parser_required(&args, CMDLINE_PARSER_PACKAGE) != 0) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
SET_IF_GIVEN(zconf.output_filename, output_file);
|
|
SET_IF_GIVEN(zconf.blacklist_filename, blacklist_file);
|
|
SET_IF_GIVEN(zconf.whitelist_filename, whitelist_file);
|
|
SET_IF_GIVEN(zconf.probe_args, probe_args);
|
|
SET_IF_GIVEN(zconf.output_args, output_args);
|
|
SET_IF_GIVEN(zconf.iface, interface);
|
|
SET_IF_GIVEN(zconf.max_runtime, max_runtime);
|
|
SET_IF_GIVEN(zconf.max_results, max_results);
|
|
SET_IF_GIVEN(zconf.rate, rate);
|
|
SET_IF_GIVEN(zconf.packet_streams, probes);
|
|
SET_BOOL(zconf.dryrun, dryrun);
|
|
SET_BOOL(zconf.quiet, quiet);
|
|
SET_BOOL(zconf.summary, summary);
|
|
zconf.cooldown_secs = args.cooldown_time_arg;
|
|
zconf.senders = args.sender_threads_arg;
|
|
zconf.log_level = args.verbosity_arg;
|
|
|
|
zconf.output_module = get_output_module_by_name(args.output_module_arg);
|
|
if (!zconf.output_module) {
|
|
fprintf(stderr, "%s: specified output module (%s) does not exist\n",
|
|
args.output_module_arg, CMDLINE_PARSER_PACKAGE);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
zconf.probe_module = get_probe_module_by_name(args.probe_module_arg);
|
|
if (!zconf.probe_module) {
|
|
fprintf(stderr, "%s: specified probe module (%s) does not exist\n",
|
|
CMDLINE_PARSER_PACKAGE, args.probe_module_arg);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (zconf.probe_module->port_args) {
|
|
if (args.source_port_given) {
|
|
char *dash = strchr(args.source_port_arg, '-');
|
|
if (dash) { // range
|
|
*dash = '\0';
|
|
zconf.source_port_first = atoi(args.source_port_arg);
|
|
enforce_range("starting source-port", zconf.source_port_first, 0, 0xFFFF);
|
|
zconf.source_port_last = atoi(dash+1);
|
|
enforce_range("ending source-port", zconf.source_port_last, 0, 0xFFFF);
|
|
if (zconf.source_port_first > zconf.source_port_last) {
|
|
fprintf(stderr, "%s: invalid source port range: "
|
|
"last port is less than first port\n",
|
|
CMDLINE_PARSER_PACKAGE);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
} else { // single port
|
|
int port = atoi(args.source_port_arg);
|
|
enforce_range("source-port", port, 0, 0xFFFF);
|
|
zconf.source_port_first = port;
|
|
zconf.source_port_last = port;
|
|
}
|
|
}
|
|
if (!args.target_port_given) {
|
|
fprintf(stderr, "%s: target port is required for this type of probe\n",
|
|
CMDLINE_PARSER_PACKAGE);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
enforce_range("target-port", args.target_port_arg, 0, 0xFFFF);
|
|
zconf.target_port = args.target_port_arg;
|
|
}
|
|
if (args.source_ip_given) {
|
|
char *dash = strchr(args.source_ip_arg, '-');
|
|
if (dash) { // range
|
|
*dash = '\0';
|
|
zconf.source_ip_first = args.source_ip_arg;
|
|
zconf.source_ip_last = dash+1;
|
|
} else { // single address
|
|
zconf.source_ip_first = args.source_ip_arg;
|
|
zconf.source_ip_last = args.source_ip_arg;
|
|
}
|
|
}
|
|
if (args.gateway_mac_given) {
|
|
if (!parse_mac(zconf.gw_mac, args.gateway_mac_arg)) {
|
|
fprintf(stderr, "%s: invalid MAC address `%s'\n",
|
|
CMDLINE_PARSER_PACKAGE, args.gateway_mac_arg);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
zconf.gw_mac_set = 1;
|
|
}
|
|
if (args.seed_given) {
|
|
zconf.seed = args.seed_arg;
|
|
zconf.use_seed = 1;
|
|
}
|
|
if (args.bandwidth_given) {
|
|
// Supported: G,g=*1000000000; M,m=*1000000 K,k=*1000 bits per second
|
|
zconf.bandwidth = atoi(args.bandwidth_arg);
|
|
char *suffix = args.bandwidth_arg;
|
|
while (*suffix >= '0' && *suffix <= '9') {
|
|
suffix++;
|
|
}
|
|
if (*suffix) {
|
|
switch (*suffix) {
|
|
case 'G': case 'g':
|
|
zconf.bandwidth *= 1000000000;
|
|
break;
|
|
case 'M': case 'm':
|
|
zconf.bandwidth *= 1000000;
|
|
break;
|
|
case 'K': case 'k':
|
|
zconf.bandwidth *= 1000;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "%s: unknown bandwidth suffix '%s' "
|
|
"(supported suffixes are G, M and K)\n",
|
|
CMDLINE_PARSER_PACKAGE, suffix);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
}
|
|
if (args.max_targets_given) {
|
|
errno = 0;
|
|
char *end;
|
|
double v = strtod(args.max_targets_arg, &end);
|
|
if (end == args.max_targets_arg || errno != 0) {
|
|
fprintf(stderr, "%s: can't convert max-targets to a number\n",
|
|
CMDLINE_PARSER_PACKAGE);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (end[0] == '%' && end[1] == '\0') {
|
|
// treat as percentage
|
|
v = v * (1L << 32) / 100.;
|
|
} else if (end[0] != '\0') {
|
|
fprintf(stderr, "%s: extra characters after max-targets\n",
|
|
CMDLINE_PARSER_PACKAGE);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if (v <= 0) {
|
|
zconf.max_targets = 0;
|
|
}
|
|
else if (v >= (1L << 32)) {
|
|
zconf.max_targets = 0xFFFFFFFF;
|
|
} else {
|
|
zconf.max_targets = v;
|
|
}
|
|
}
|
|
|
|
start_zmap();
|
|
|
|
cmdline_parser_free(&args);
|
|
free(params);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|