send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t *pcap2, int cache_file_idx2)
{
    struct timeval print_delta, now, last_pkt_ts;
    tcpreplay_opt_t *options = ctx->options;
    tcpreplay_stats_t *stats = &ctx->stats;
    COUNTER packetnum = 0;
    COUNTER limit_send = options->limit_send;
    int cache_file_idx;
    struct pcap_pkthdr pkthdr1, pkthdr2;
    u_char *pktdata1 = NULL, *pktdata2 = NULL, *pktdata = NULL;
    sendpacket_t *sp;
    COUNTER pktlen;
    packet_cache_t *cached_packet1 = NULL, *cached_packet2 = NULL;
    packet_cache_t **prev_packet1 = NULL, **prev_packet2 = NULL;
    struct pcap_pkthdr *pkthdr_ptr;
    int datalink;
    COUNTER end_us;
    COUNTER skip_length = 0;
    bool top_speed = (options->speed.mode == speed_topspeed ||
            (options->speed.mode == speed_mbpsrate && options->speed.speed == 0));
    bool now_is_now = true;

    gettimeofday(&now, NULL);
    if (!timerisset(&stats->start_time)) {
        TIMEVAL_SET(&stats->start_time, &now);
        if (ctx->options->stats >= 0) {
            char buf[64];
            if (format_date_time(&stats->start_time, buf, sizeof(buf)) > 0)
                printf("Dual test start: %s ...\n", buf);
        }
    }

    ctx->skip_packets = 0;
    timerclear(&last_pkt_ts);
    timerclear(&stats->first_packet_sent_wall_time);
    if (options->limit_time > 0)
        end_us = TIMEVAL_TO_MICROSEC(&stats->start_time) +
            SEC_TO_MICROSEC(options->limit_time);
    else
        end_us = 0;

    if (options->preload_pcap) {
        prev_packet1 = &cached_packet1;
        prev_packet2 = &cached_packet2;
    } else {
        prev_packet1 = NULL;
        prev_packet2 = NULL;
    }

    pktdata1 = get_next_packet(ctx, pcap1, &pkthdr1, cache_file_idx1, prev_packet1);
    pktdata2 = get_next_packet(ctx, pcap2, &pkthdr2, cache_file_idx2, prev_packet2);

    /* MAIN LOOP 
     * Keep sending while we have packets or until
     * we've sent enough packets
     */
    while (!ctx->abort &&
            !(pktdata1 == NULL && pktdata2 == NULL)) {

        now_is_now = false;
        packetnum++;

        /* figure out which pcap file we need to process next 
         * when get_next_packet() returns null for pktdata, the pkthdr 
         * will still have the old values from the previous call.  This
         * means we can't always trust the timestamps to tell us which
         * file to process.
         */
        if (pktdata1 == NULL) {
            /* file 2 is next */
            sp = ctx->intf2;
            datalink = options->file_cache[cache_file_idx2].dlt;
            pkthdr_ptr = &pkthdr2;
            cache_file_idx = cache_file_idx2;
            pktdata = pktdata2;
        } else if (pktdata2 == NULL) {
            /* file 1 is next */
            sp = ctx->intf1;
            datalink = options->file_cache[cache_file_idx1].dlt;
            pkthdr_ptr = &pkthdr1;
            cache_file_idx = cache_file_idx1;
            pktdata = pktdata1;
        } else if (timercmp(&pkthdr1.ts, &pkthdr2.ts, <=)) {
            /* file 1 is next */
            sp = ctx->intf1;
            datalink = options->file_cache[cache_file_idx1].dlt;
            pkthdr_ptr = &pkthdr1;
            cache_file_idx = cache_file_idx1;
            pktdata = pktdata1;
        } else {
            /* file 2 is next */
            sp = ctx->intf2;
            datalink = options->file_cache[cache_file_idx2].dlt;
            pkthdr_ptr = &pkthdr2;
            cache_file_idx = cache_file_idx2;
            pktdata = pktdata2;
        }

#if defined TCPREPLAY || defined TCPREPLAY_EDIT
        /* do we use the snaplen (caplen) or the "actual" packet len? */
        pktlen = options->use_pkthdr_len ? (COUNTER)pkthdr_ptr->len : (COUNTER)pkthdr_ptr->caplen;
#elif TCPBRIDGE
        pktlen = (COUNTER)pkthdr_ptr->caplen;
#else
#error WTF???  We should not be here!
#endif

        dbgx(2, "packet " COUNTER_SPEC " caplen " COUNTER_SPEC, packetnum, pktlen);

#if defined TCPREPLAY && defined TCPREPLAY_EDIT
        if (tcpedit_packet(tcpedit, &pkthdr_ptr, &pktdata, sp->cache_dir) == -1) {
            errx(-1, "Error editing packet #" COUNTER_SPEC ": %s", packetnum, tcpedit_geterr(tcpedit));
        }
        pktlen = options->use_pkthdr_len ? (COUNTER)pkthdr_ptr->len : (COUNTER)pkthdr_ptr->caplen;
#endif

        if (ctx->options->unique_ip && ctx->unique_iteration &&
                ctx->unique_iteration > ctx->last_unique_iteration) {
            /* edit packet to ensure every pass is unique */
            if (fast_edit_packet(pkthdr_ptr, &pktdata, ctx->unique_iteration - 1,
                    options->file_cache[cache_file_idx].cached, datalink) == -1) {
                ++stats->failed;
                continue;
            }
        }

        /* update flow stats */
        if (options->flow_stats && !options->file_cache[cache_file_idx].cached)
            update_flow_stats(ctx, sp, pkthdr_ptr, pktdata, datalink);

        /*
         * this accelerator improves performance by avoiding expensive
         * time stamps during periods where we have fallen behind in our
         * sending
         */
        if (skip_length && pktlen < skip_length) {
            skip_length -= pktlen;
        } else if (ctx->skip_packets) {
            --ctx->skip_packets;
        } else {
            /*
             * time stamping is expensive, but now is the
             * time to do it.
             */
            dbgx(4, "This packet time: " TIMEVAL_FORMAT, pkthdr_ptr->ts.tv_sec,
                    pkthdr_ptr->ts.tv_usec);
            skip_length = 0;
            ctx->skip_packets = 0;

            if (options->speed.mode == speed_multiplier) {
                if(!timerisset(&stats->first_packet_sent_wall_time)) {
                    /* We're sending the first packet, so we have an absolute time reference. */
                    TIMEVAL_SET(&stats->first_packet_sent_wall_time, &now);
                    TIMEVAL_SET(&stats->first_packet_pcap_timestamp, &pkthdr_ptr->ts);
                }

                if (!timerisset(&last_pkt_ts)) {
                    TIMEVAL_SET(&last_pkt_ts, &pkthdr_ptr->ts);
                } else if (timercmp(&pkthdr_ptr->ts, &last_pkt_ts, >)) {
                    /* pkt_ts_delta is the packet time stamp difference since the first packet */
                    timersub(&pkthdr_ptr->ts, &stats->first_packet_pcap_timestamp, &stats->pkt_ts_delta);

                    /* time_delta is the wall time difference since sending the first packet */
                    timersub(&now, &stats->first_packet_sent_wall_time, &stats->time_delta);

                    TIMEVAL_SET(&last_pkt_ts, &pkthdr_ptr->ts);
                }
            }

            if (!top_speed) {
                gettimeofday(&now, NULL);
                now_is_now = true;
            }

            /*
             * Only if the current packet is not late.
             *
             * This also sets skip_length and skip_packets which will avoid
             * timestamping for a given number of packets.
             */
            calc_sleep_time(ctx, &stats->pkt_ts_delta, &stats->time_delta,
                    pktlen, sp, packetnum, &stats->end_time,
                    TIMEVAL_TO_MICROSEC(&stats->start_time), &skip_length);

            /*
             * Track the time of the "last packet sent".
             *
             * A number of 3rd party tools generate bad timestamps which go backwards
             * in time.  Hence, don't update the "last" unless pkthdr_ptr->ts > last
             */
            if (timercmp(&stats->time_delta, &stats->pkt_ts_delta, <))
                TIMEVAL_SET(&stats->time_delta, &stats->pkt_ts_delta);

            /*
             * we know how long to sleep between sends, now do it.
             */
            if (!top_speed)
                tcpr_sleep(ctx, sp, &ctx->nap, &now);
        }

#ifdef ENABLE_VERBOSE
        /* do we need to print the packet via tcpdump? */
        if (options->verbose)
            tcpdump_print(options->tcpdump, pkthdr_ptr, pktdata);
#endif

        dbgx(2, "Sending packet #" COUNTER_SPEC, packetnum);
        /* write packet out on network */
        if (sendpacket(sp, pktdata, pktlen, pkthdr_ptr) < (int)pktlen) {
            warnx("Unable to send packet: %s", sendpacket_geterr(sp));
            break;
        }

        /*
         * Mark the time when we sent the last packet
         */
        TIMEVAL_SET(&stats->end_time, &now);

        ++stats->pkts_sent;
        stats->bytes_sent += pktlen;

        /* print stats during the run? */
        if (options->stats > 0) {
            if (! timerisset(&stats->last_print)) {
                TIMEVAL_SET(&stats->last_print, &now);
            } else {
                timersub(&now, &stats->last_print, &print_delta);
                if (print_delta.tv_sec >= options->stats) {
                    TIMEVAL_SET(&stats->end_time, &now);
                    packet_stats(stats);
                    TIMEVAL_SET(&stats->last_print, &now);
                }
            }
        }

#if defined HAVE_NETMAP
        if (sp->first_packet || timesisset(&ctx->nap)) {
            wake_send_queues(sp, options);
            sp->first_packet = false;
        }
#endif

        /* get the next packet for this file handle depending on which we last used */
        if (sp == ctx->intf2) {
            pktdata2 = get_next_packet(ctx, pcap2, &pkthdr2, cache_file_idx2, prev_packet2);
        } else {
            pktdata1 = get_next_packet(ctx, pcap1, &pkthdr1, cache_file_idx1, prev_packet1);
        }

        /* stop sending based on the duration limit... */
        if ((end_us > 0 && (COUNTER)TIMEVAL_TO_MICROSEC(&now) > end_us) ||
                /* ... or stop sending based on the limit -L? */
                (limit_send > 0 && stats->pkts_sent >= limit_send)) {
            ctx->abort = true;
        }
    } /* while */

#ifdef HAVE_NETMAP
    /* when completing test, wait until the last packet is sent */
    if (options->netmap && (ctx->abort || options->loop == 1)) {
        while (ctx->intf1 && !netmap_tx_queues_empty(ctx->intf1)) {
            gettimeofday(&now, NULL);
            now_is_now = true;
        }

        while (ctx->intf2 && !netmap_tx_queues_empty(ctx->intf2)) {
            gettimeofday(&now, NULL);
            now_is_now = true;
        }
    }
#endif /* HAVE_NETMAP */

    if (!now_is_now)
        gettimeofday(&now, NULL);

    TIMEVAL_SET(&stats->end_time, &now);

    increment_iteration(ctx);
}