2972bbefdc
When a user specifies a `-i tun0` flag for a VPN interface, this interface expects raw IP packets, not ethernet frames. The --vpn option allows you to send only IP packets to the specified interface, and also performs some hacks on the receive end to ensure we can process the results. TODO: malloc a fake ethernet header for probe_modules.process_packet (or better yet, make process_packet always take an IP packet, they all seem to be implicitly doing that anyway).
631 lines
19 KiB
C
631 lines
19 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 <string.h>
|
|
#include <unistd.h>
|
|
#include <assert.h>
|
|
#include <sched.h>
|
|
#include <errno.h>
|
|
|
|
#include <pwd.h>
|
|
#include <net/if.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <sys/types.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 "get_gateway.h"
|
|
|
|
#include "output_modules/output_modules.h"
|
|
#include "probe_modules/probe_modules.h"
|
|
|
|
pthread_mutex_t cpu_affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
pthread_mutex_t recv_ready_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
// splits comma delimited string into char*[]. Does not handle
|
|
// escaping or complicated setups: designed to process a set
|
|
// of fields that the user wants output
|
|
static void split_string(char* in, int *len, char***results)
|
|
{
|
|
char** fields = calloc(MAX_FIELDS, sizeof(char*));
|
|
memset(fields, 0, sizeof(fields));
|
|
int retvlen = 0;
|
|
char *currloc = in;
|
|
// parse csv into a set of strings
|
|
while (1) {
|
|
size_t len = strcspn(currloc, ", ");
|
|
if (len == 0) {
|
|
currloc++;
|
|
} else {
|
|
char *new = malloc(len+1);
|
|
assert(new);
|
|
strncpy(new, currloc, len);
|
|
new[len] = '\0';
|
|
fields[retvlen++] = new;
|
|
assert(fields[retvlen-1]);
|
|
}
|
|
if (len == strlen(currloc)) {
|
|
break;
|
|
}
|
|
currloc += len;
|
|
}
|
|
*results = fields;
|
|
*len = retvlen;
|
|
}
|
|
|
|
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(void *arg)
|
|
{
|
|
uintptr_t v = (uintptr_t) arg;
|
|
int sock = (int) v & 0xFFFF;
|
|
set_cpu();
|
|
send_run(sock);
|
|
return NULL;
|
|
}
|
|
|
|
static void* start_recv(__attribute__((unused)) void *arg)
|
|
{
|
|
set_cpu();
|
|
recv_run(&recv_ready_mutex);
|
|
return NULL;
|
|
}
|
|
|
|
static void drop_privs()
|
|
{
|
|
struct passwd *pw;
|
|
if ((pw = getpwnam("nobody")) != NULL) {
|
|
if (setuid(pw->pw_uid) == 0) {
|
|
return; // success
|
|
}
|
|
}
|
|
log_fatal("zmap", "Couldn't change UID to 'nobody'");
|
|
}
|
|
|
|
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, (long unsigned int) 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_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, zconf.output_fields,
|
|
zconf.output_fields_len);
|
|
}
|
|
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++) {
|
|
uintptr_t sock;
|
|
if (zconf.dryrun) {
|
|
sock = get_dryrun_socket();
|
|
} else {
|
|
sock = get_socket();
|
|
}
|
|
|
|
int r = pthread_create(&tsend[i], NULL, start_send, (void*) sock);
|
|
if (r != 0) {
|
|
log_fatal("zmap", "unable to create send thread");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
if (!zconf.quiet) {
|
|
int r = pthread_create(&tmon, NULL, start_mon, NULL);
|
|
if (r != 0) {
|
|
log_fatal("zmap", "unable to create monitor thread");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
drop_privs();
|
|
|
|
// wait for completion
|
|
for (int i=0; i < zconf.senders; i++) {
|
|
int r = pthread_join(tsend[i], NULL);
|
|
if (r != 0) {
|
|
log_fatal("zmap", "unable to join send thread");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
log_debug("zmap", "senders finished");
|
|
r = 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);
|
|
}
|
|
|
|
zconf.log_level = args.verbosity_arg;
|
|
log_init(stderr, zconf.log_level);
|
|
log_trace("zmap", "zmap main thread started");
|
|
|
|
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 (args.vpn_given) {
|
|
zconf.send_ip_pkts = 1;
|
|
zconf.gw_mac_set = 1;
|
|
memset(zconf.gw_mac, 0, IFHWADDRLEN);
|
|
}
|
|
if (cmdline_parser_required(&args, CMDLINE_PARSER_PACKAGE) != 0) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
// parse the provided probe and output module s.t. that we can support
|
|
// other command-line helpers (e.g. probe help)
|
|
if (!args.output_module_given) {
|
|
zconf.output_module = get_output_module_by_name("csv");
|
|
zconf.raw_output_fields = (char*) "saddr";
|
|
zconf.filter_duplicates = 1;
|
|
zconf.filter_unsuccessful = 1;
|
|
} else if (!strcmp(args.output_module_arg, "simple_file")) {
|
|
log_warn("zmap", "the simple_file output interface has been deprecated and "
|
|
"will be removed in the future. Users should use the csv "
|
|
"output module. Newer scan options such as output-fields "
|
|
"are not supported with this output module.");
|
|
zconf.output_module = get_output_module_by_name("csv");
|
|
zconf.raw_output_fields = (char*) "saddr";
|
|
zconf.filter_duplicates = 1;
|
|
zconf.filter_unsuccessful = 1;
|
|
} else if (!strcmp(args.output_module_arg, "extended_file")) {
|
|
log_warn("zmap", "the extended_file output interface has been deprecated and "
|
|
"will be removed in the future. Users should use the csv "
|
|
"output module. Newer scan options such as output-fields "
|
|
"are not supported with this output module.");
|
|
zconf.output_module = get_output_module_by_name("csv");
|
|
zconf.raw_output_fields = (char*) "classification, saddr, "
|
|
"daddr, sport, dport, "
|
|
"seqnum, acknum, cooldown, "
|
|
"repeat, timestamp-str";
|
|
zconf.filter_duplicates = 0;
|
|
} else {
|
|
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",
|
|
CMDLINE_PARSER_PACKAGE, args.output_module_arg);
|
|
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);
|
|
}
|
|
|
|
// now that we know the probe module, let's find what it supports
|
|
memset(&zconf.fsconf, 0, sizeof(struct fieldset_conf));
|
|
// the set of fields made available to a user is constructed
|
|
// of IP header fields + probe module fields + system fields
|
|
fielddefset_t *fds = &(zconf.fsconf.defs);
|
|
gen_fielddef_set(fds, (fielddef_t*) &(ip_fields),
|
|
4);
|
|
gen_fielddef_set(fds, zconf.probe_module->fields,
|
|
zconf.probe_module->numfields);
|
|
gen_fielddef_set(fds, (fielddef_t*) &(sys_fields),
|
|
5);
|
|
if (args.list_output_fields_given) {
|
|
for (int i = 0; i < fds->len; i++) {
|
|
printf("%s (%s): %s\n", fds->fielddefs[i].name,
|
|
fds->fielddefs[i].type,
|
|
fds->fielddefs[i].desc);
|
|
}
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
// find the fields we need for the framework
|
|
zconf.fsconf.success_index =
|
|
fds_get_index_by_name(fds, (char*) "success");
|
|
if (zconf.fsconf.success_index < 0) {
|
|
log_fatal("fieldset", "probe module does not supply "
|
|
"required success field.");
|
|
}
|
|
zconf.fsconf.classification_index =
|
|
fds_get_index_by_name(fds, (char*) "classification");
|
|
if (zconf.fsconf.classification_index < 0) {
|
|
log_fatal("fieldset", "probe module does not supply "
|
|
"required packet classification field.");
|
|
}
|
|
// process the list of requested output fields.
|
|
if (args.output_fields_given) {
|
|
zconf.raw_output_fields = args.output_fields_arg;
|
|
} else if (!zconf.raw_output_fields) {
|
|
zconf.raw_output_fields = (char*) "saddr";
|
|
}
|
|
split_string(zconf.raw_output_fields, &(zconf.output_fields_len),
|
|
&(zconf.output_fields));
|
|
for (int i=0; i < zconf.output_fields_len; i++) {
|
|
log_debug("zmap", "requested output field (%i): %s",
|
|
i,
|
|
zconf.output_fields[i]);
|
|
}
|
|
// generate a translation that can be used to convert output
|
|
// from a probe module to the input for an output module
|
|
fs_generate_fieldset_translation(&zconf.fsconf.translation,
|
|
&zconf.fsconf.defs, zconf.output_fields,
|
|
zconf.output_fields_len);
|
|
|
|
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;
|
|
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);
|
|
|
|
|
|
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 * ((unsigned long long int)1 << 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 >= ((unsigned long long int)1 << 32)) {
|
|
zconf.max_targets = 0xFFFFFFFF;
|
|
} else {
|
|
zconf.max_targets = v;
|
|
}
|
|
}
|
|
|
|
start_zmap();
|
|
|
|
cmdline_parser_free(&args);
|
|
free(params);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|