diff --git a/util/exanic-capture.c b/util/exanic-capture.c index e585046..fd9574d 100644 --- a/util/exanic-capture.c +++ b/util/exanic-capture.c @@ -18,7 +18,7 @@ #include "pcap-structures.h" -typedef enum +typedef enum { FORMAT_PCAP = 0, FORMAT_ERF = 1, @@ -456,23 +456,34 @@ static int set_promiscuous_mode(exanic_t *exanic, int port_number, int enable) return 0; } -int main(int argc, char *argv[]) +struct exanic_rx_context { - const char *interface = NULL; - const char *savefile = NULL; - FILE *savefp = NULL; char device[16]; int port_number; exanic_t *exanic; exanic_rx_t *rx; + int set_promisc; + exanic_cycles_t utc_offset_cycles; +}; + +#define EXA_MAX_IFACES 8 + +int main(int argc, char *argv[]) +{ + const char *savefile = NULL; + FILE *savefp = NULL; + struct exanic_rx_context rx_ctxs[EXA_MAX_IFACES]; + int rx_ctxs_no = 0; + int rx_ctx_idx = 0; char rx_buf[16384]; ssize_t rx_size; int status; exanic_cycles32_t timestamp; + int utc_offset_sec = 0; struct timespec ts; struct exanic_timespecps tsps; int hw_tstamp = 0, nsec_pcap = 0, snaplen = sizeof(rx_buf), flush = 0; - int promisc = 1, set_promisc, filter; + int promisc = 1, filter; unsigned long rx_success = 0, rx_aborted = 0, rx_corrupt = 0, rx_hwovfl = 0, rx_swovfl = 0, rx_other = 0; file_format_type file_format = FORMAT_PCAP; @@ -481,12 +492,30 @@ int main(int argc, char *argv[]) char file_name_buf[4096]; int c; - while ((c = getopt(argc, argv, "i:w:s:C:F:pHNh?")) != -1) + memset(&rx_ctxs, 0, sizeof(rx_ctxs)); + + while ((c = getopt(argc, argv, "i:w:s:C:F:U:pHNh?")) != -1) { switch (c) { case 'i': - interface = optarg; + if (rx_ctxs_no >= EXA_MAX_IFACES) + { + fprintf(stderr, "maximum interfaces supported: %d\n", EXA_MAX_IFACES); + return 1; + } + + if (exanic_find_port_by_interface_name( + optarg, rx_ctxs[rx_ctxs_no].device, 16, &rx_ctxs[rx_ctxs_no].port_number) != 0 + && + parse_device_port( + optarg, rx_ctxs[rx_ctxs_no].device, &rx_ctxs[rx_ctxs_no].port_number) != 0) + { + fprintf(stderr, "%s: no such interface or not an ExaNIC\n", optarg); + return 1; + } + + ++rx_ctxs_no; break; case 'w': savefile = optarg; @@ -507,6 +536,9 @@ int main(int argc, char *argv[]) else goto usage_error; break; + case 'U': + utc_offset_sec = atoi(optarg); + break; case 'p': promisc = 0; break; @@ -521,16 +553,9 @@ int main(int argc, char *argv[]) } } - if (interface == NULL) + if (rx_ctxs_no == 0) goto usage_error; - if (exanic_find_port_by_interface_name(interface, device, 16, &port_number) != 0 - && parse_device_port(interface, device, &port_number) != 0) - { - fprintf(stderr, "%s: no such interface or not an ExaNIC\n", interface); - return 1; - } - if (savefile != NULL) { if (strcmp(savefile, "-") == 0) @@ -554,31 +579,42 @@ int main(int argc, char *argv[]) file_size = write_pcap_header(savefp, nsec_pcap, snaplen); } - /* Get the exanic handle */ - exanic = exanic_acquire_handle(device); - if (exanic == NULL) + for (int i = 0; i < rx_ctxs_no; ++i) { - fprintf(stderr, "%s: %s\n", device, exanic_get_last_error()); - goto err_acquire_handle; - } + /* Get the exanic handle */ + rx_ctxs[i].exanic = exanic_acquire_handle(rx_ctxs[i].device); + if (rx_ctxs[i].exanic == NULL) + { + fprintf(stderr, "%s: %s\n", rx_ctxs[i].device, exanic_get_last_error()); + goto err_acquire_handle; + } - filter = optind < argc; - if (filter) - rx = exanic_acquire_unused_filter_buffer(exanic, port_number); - else - rx = exanic_acquire_rx_buffer(exanic, port_number, 0); + filter = optind < argc; + if (filter) + rx_ctxs[i].rx = exanic_acquire_unused_filter_buffer(rx_ctxs[i].exanic, rx_ctxs[i].port_number); + else + rx_ctxs[i].rx = exanic_acquire_rx_buffer(rx_ctxs[i].exanic, rx_ctxs[i].port_number, 0); - if (rx == NULL) - { - fprintf(stderr, "%s:%d: %s\n", device, port_number, - exanic_get_last_error()); - goto err_acquire_rx; - } + if (rx_ctxs[i].rx == NULL) + { + fprintf(stderr, "%s:%d: %s\n", rx_ctxs[i].device, rx_ctxs[i].port_number, + exanic_get_last_error()); + goto err_acquire_rx; + } + + if (filter && !apply_filters(rx_ctxs[i].exanic, rx_ctxs[i].rx, &argv[optind], argc-optind)) + goto err_apply_filters; - if (filter && !apply_filters(exanic, rx, &argv[optind], argc-optind)) - goto err_apply_filters; + rx_ctxs[i].set_promisc = promisc && !exanic_get_promiscuous_mode(rx_ctxs[i].exanic, rx_ctxs[i].port_number); - set_promisc = promisc && !exanic_get_promiscuous_mode(exanic, port_number); + if (rx_ctxs[i].set_promisc) + { + if (set_promiscuous_mode(rx_ctxs[i].exanic, rx_ctxs[i].port_number, 1) == -1) + rx_ctxs[i].set_promisc = 0; + } + + rx_ctxs[i].utc_offset_cycles = rx_ctxs[i].exanic->tick_hz * utc_offset_sec; + } signal(SIGHUP, signal_handler); signal(SIGINT, signal_handler); @@ -586,26 +622,24 @@ int main(int argc, char *argv[]) signal(SIGALRM, signal_handler); signal(SIGTERM, signal_handler); - if (set_promisc) - { - if (set_promiscuous_mode(exanic, port_number, 1) == -1) - set_promisc = 0; - } - /* Start reading from the rx buffer */ while (run) { - rx_size = exanic_receive_frame_ex(rx, rx_buf, sizeof(rx_buf), + rx_size = exanic_receive_frame_ex(rx_ctxs[rx_ctx_idx].rx, rx_buf, sizeof(rx_buf), ×tamp, &status); if (rx_size < 0 && status == EXANIC_RX_FRAME_OK) + { + rx_ctx_idx = (rx_ctx_idx + 1) % rx_ctxs_no; continue; + } /* Get timestamp */ if (rx_size > 0 && hw_tstamp) { - const uint64_t timestamp64 = exanic_expand_timestamp(exanic, timestamp); - exanic_cycles_to_timespecps(exanic, timestamp64, &tsps); + const uint64_t timestamp64 = exanic_expand_timestamp(rx_ctxs[rx_ctx_idx].exanic, timestamp) + + rx_ctxs[rx_ctx_idx].utc_offset_cycles; /* Add optional UTC offset in cycles */ + exanic_cycles_to_timespecps(rx_ctxs[rx_ctx_idx].exanic, timestamp64, &tsps); } else { @@ -644,7 +678,7 @@ int main(int argc, char *argv[]) snaplen, savefp); else if (file_format == FORMAT_ERF) file_size += write_erf_packet(rx_buf, rx_size, &tsps, - port_number, + rx_ctxs[rx_ctx_idx].port_number, snaplen, savefp); if (flush) fflush(savefp); @@ -694,23 +728,27 @@ int main(int argc, char *argv[]) rx_other++; } - if (set_promisc) - set_promiscuous_mode(exanic, port_number, 0); - fprintf(stderr, "%s: received=%lu corrupt=%lu aborted=%lu hw_lost=%lu sw_lost=%lu other=%lu\n", argv[0], rx_success, rx_corrupt, rx_aborted, rx_hwovfl, rx_swovfl, rx_other); - exanic_release_rx_buffer(rx); - exanic_release_handle(exanic); if (savefp != NULL) fclose(savefp); return 0; err_open_next_file: err_apply_filters: - exanic_release_rx_buffer(rx); err_acquire_rx: - exanic_release_handle(exanic); + for (int i = 0; i < rx_ctxs_no; ++i) + { + if (rx_ctxs[i].set_promisc && rx_ctxs[i].exanic) + set_promiscuous_mode(rx_ctxs[i].exanic, rx_ctxs[i].port_number, 0); + + if (rx_ctxs[i].rx) + exanic_release_rx_buffer(rx_ctxs[i].rx); + + if (rx_ctxs[i].exanic) + exanic_release_handle(rx_ctxs[i].exanic); + } err_acquire_handle: if (savefp != NULL) fclose(savefp); @@ -718,7 +756,7 @@ int main(int argc, char *argv[]) return 1; usage_error: - fprintf(stderr, "Usage: %s -i interface\n", argv[0]); + fprintf(stderr, "Usage: %s -i interface...\n", argv[0]); fprintf(stderr, " [-w savefile] [-s snaplen] [-C file_size]\n"); fprintf(stderr, " [-F file_format] [-p] [-H] [-N] [filter...]\n"); fprintf(stderr, " -i: specify Linux interface (e.g. eth0) or ExaNIC port name (e.g. exanic0:0)\n"); @@ -726,6 +764,7 @@ int main(int argc, char *argv[]) fprintf(stderr, " -s: maximum data length to capture\n"); fprintf(stderr, " -C: file size at which to start a new save file (in millions of bytes)\n"); fprintf(stderr, " -F: file format [pcap|erf] (default is pcap)\n"); + fprintf(stderr, " -U: UTC offset to add to hardware timestamp (in seconds)\n"); fprintf(stderr, " -p: do not attempt to put interface in promiscuous mode\n"); fprintf(stderr, " -H: use hardware timestamps (requires exanic-clock-sync or exanic-ptpd)\n"); fprintf(stderr, " -N: write nanosecond-resolution pcap format\n\n");