summaryrefslogtreecommitdiffstats
path: root/src/bin/perfdhcp/avalanche_scen.cc
blob: 7e0fd6879f6f0c5e0eb9351eeece2e6c364efcbe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <config.h>

#include <perfdhcp/avalanche_scen.h>


#include <boost/date_time/posix_time/posix_time.hpp>

using namespace std;
using namespace boost::posix_time;
using namespace isc;
using namespace isc::dhcp;


namespace isc {
namespace perfdhcp {

int
AvalancheScen::resendPackets(ExchangeType xchg_type) {
    const StatsMgr& stats_mgr(tc_.getStatsMgr());

    // get list of sent packets that potentially need to be resent
    auto sent_packets_its = stats_mgr.getSentPackets(xchg_type);
    auto begin_it = std::get<0>(sent_packets_its);
    auto end_it = std::get<1>(sent_packets_its);

    auto& retrans = retransmissions_[xchg_type];
    auto& start_times = start_times_[xchg_type];

    int still_left_cnt = 0;
    int current_cycle_resent_cnt = 0;
    for (auto it = begin_it; it != end_it; ++it) {
        still_left_cnt++;

        dhcp::PktPtr pkt = *it;
        auto trans_id = pkt->getTransid();

        // get some things from previous retransmissions
        auto start_time = pkt->getTimestamp();
        int current_pkt_resent_cnt = 0;
        auto r_it = retrans.find(trans_id);
        if (r_it != retrans.end()) {
            start_time = (*start_times.find(trans_id)).second;
            current_pkt_resent_cnt = (*r_it).second;
        } else {
            start_times[trans_id] = start_time;
        }

        // estimate back off time for resending this packet
        int delay = (1 << current_pkt_resent_cnt); // in seconds
        if (delay > 64) {
            delay = 64;
        }
        delay *= 1000;  // to miliseconds
        delay += random() % 2000 - 1000;  // adjust by random from -1000..1000 range

        // if back-off time passed then resend
        auto now = microsec_clock::universal_time();
        if (now - start_time > milliseconds(delay)) {
            current_cycle_resent_cnt++;
            total_resent_++;

            // do resend packet
            if (options_.getIpVersion() == 4) {
                Pkt4Ptr pkt4 = boost::dynamic_pointer_cast<Pkt4>(pkt);
                socket_.send(pkt4);
            } else {
                Pkt6Ptr pkt6 = boost::dynamic_pointer_cast<Pkt6>(pkt);
                socket_.send(pkt6);
            }

            // restore sending time of original packet
            pkt->setTimestamp(start_time);

            current_pkt_resent_cnt++;
            retrans[trans_id] = current_pkt_resent_cnt;
        }
    }
    if (current_cycle_resent_cnt > 0) {
        auto now = microsec_clock::universal_time();
        std::cout << now << " " << xchg_type << ": still waiting for "
                  << still_left_cnt << " answers, resent " << current_cycle_resent_cnt
                  << ", retrying " << retrans.size() << std::endl;
    }
    return still_left_cnt;
}



int
AvalancheScen::run() {
    // First indicated number of DISCOVER packets eg. 4000 are sent.
    // Then in a loop responses to received packets (this is
    // consumeReceivedPackets()) are sent and then for every 200ms it is checked
    // if reponses to sent packets were received. If not packets are resent.
    // This happens in resendPackets() method. For each packet it is checked
    // how many times it was already resent and then back off time is calculated:
    // 1, 2, 4, 8, 16, 64 (max) seconds. If estimated time has elapsed
    // from previous sending then the packet is resent. Some stats are collected
    // and printed during runtime. The whole procedure is stopeed when
    // all packets got reponses.

    uint32_t clients_num = options_.getClientsNum() == 0 ?
        1 : options_.getClientsNum();

    StatsMgr& stats_mgr(tc_.getStatsMgr());

    tc_.start();

    auto start = microsec_clock::universal_time();

    // Initiate new DHCP packet exchanges.
    tc_.sendPackets(clients_num);

    auto now = microsec_clock::universal_time();
    auto prev_cycle_time = now;
    for (;;) {
        // Pull some packets from receiver thread, process them, update some stats
        // and respond to the server if needed.
        tc_.consumeReceivedPackets();

        usleep(100);

        now = microsec_clock::universal_time();
        // Wait for 200ms between subsequent check for resending.
        // This time taken based on experiments. For times 10-30ms whole scenario
        // time significantly grows. The same for times >200ms. The optimal times
        // are between 50-200ms. \todo more research is needed.
        if (now - prev_cycle_time > milliseconds(200)) { // check if 0.2s elapsed
            prev_cycle_time = now;
            int still_left_cnt = 0;
            still_left_cnt += resendPackets(stage1_xchg_);
            if (options_.getExchangeMode() == CommandOptions::DORA_SARR) {
                still_left_cnt += resendPackets(stage2_xchg_);
            }

            if (still_left_cnt == 0) {
                break;
            }
        }

        if (tc_.interrupted()) {
            break;
        }
    }

    auto stop = microsec_clock::universal_time();
    boost::posix_time::time_period duration(start, stop);

    tc_.stop();

    tc_.printStats();

    // Print packet timestamps
    if (options_.testDiags('t')) {
        stats_mgr.printTimestamps();
    }

    // Print server id.
    if (options_.testDiags('s') && tc_.serverIdReceived()) {
        std::cout << "Server id: " << tc_.getServerId() << std::endl;
    }

    // Diagnostics flag 'e' means show exit reason.
    if (options_.testDiags('e')) {
        std::cout << "Interrupted" << std::endl;
    }

    // Calculate total stats.
    int total_sent_pkts = total_resent_; // This holds sent + resent packets counts.
    int total_rcvd_pkts = 0;  // This holds received packets count.
    // Get sent and received counts for DO/SA (stage1) exchange from StatsMgr.
    total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(stage1_xchg_);
    total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(stage1_xchg_);
    // Get sent and received counts for RA/RR (stage2) exchange from StatsMgr
    // if RA/RR was not disabled.
    if (options_.getExchangeMode() == CommandOptions::DORA_SARR) {
        total_sent_pkts += tc_.getStatsMgr().getSentPacketsNum(stage2_xchg_);
        total_rcvd_pkts += tc_.getStatsMgr().getRcvdPacketsNum(stage2_xchg_);
    }

    std::cout << "It took " << duration.length() << " to provision " << clients_num
              << " clients. " << std::endl
              << "Requests sent + resent: " << total_sent_pkts << std::endl
              << "Requests resent: " << total_resent_ << std::endl
              << "Responses received: " << total_rcvd_pkts << std::endl;

    return (0);
}

}
}