summaryrefslogtreecommitdiffstats
path: root/src/bin/perfdhcp/basic_scen.cc
blob: a9b696f61b2fbf89ace8412fe6f64cc527643e5b (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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
// Copyright (C) 2012-2021 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/basic_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 {


bool
BasicScen::checkExitConditions() {
    if (tc_.interrupted()) {
        return (true);
    }

    const StatsMgr& stats_mgr(tc_.getStatsMgr());

    // Check if test period passed.
    if (options_.getPeriod() != 0) {
        time_period period(stats_mgr.getTestPeriod());
        if (period.length().total_seconds() >= options_.getPeriod()) {
            if (options_.testDiags('e')) {
                std::cout << "reached test-period." << std::endl;
            }
            if (!tc_.waitToExit()) {
                return true;
            }
        }
    }

    bool max_requests = false;
    // Check if we reached maximum number of DISCOVER/SOLICIT sent.
    if (options_.getNumRequests().size() > 0) {
        if (stats_mgr.getSentPacketsNum(stage1_xchg_) >=
            options_.getNumRequests()[0]) {
            max_requests = true;
        }
    }
    // Check if we reached maximum number REQUEST packets.
    if (options_.getNumRequests().size() > 1) {
        if (stats_mgr.getSentPacketsNum(stage2_xchg_) >=
            options_.getNumRequests()[1]) {
            max_requests = true;
        }
    }
    if (max_requests) {
        if (options_.testDiags('e')) {
            std::cout << "Reached max requests limit." << std::endl;
        }
        if (!tc_.waitToExit()) {
            return true;
        }
    }

    // Check if we reached maximum number of drops of OFFER/ADVERTISE packets.
    bool max_drops = false;
    if (options_.getMaxDrop().size() > 0) {
        if (stats_mgr.getDroppedPacketsNum(stage1_xchg_) >=
            options_.getMaxDrop()[0]) {
            max_drops = true;
        }
    }
    // Check if we reached maximum number of drops of ACK/REPLY packets.
    if (options_.getMaxDrop().size() > 1) {
        if (stats_mgr.getDroppedPacketsNum(stage2_xchg_) >=
            options_.getMaxDrop()[1]) {
            max_drops = true;
        }
    }
    if (max_drops) {
        if (options_.testDiags('e')) {
            std::cout << "Reached maximum drops number." << std::endl;
        }
        if (!tc_.waitToExit()) {
            return true;
        }
    }

    // Check if we reached maximum drops percentage of OFFER/ADVERTISE packets.
    bool max_pdrops = false;
    if (options_.getMaxDropPercentage().size() > 0) {
        if ((stats_mgr.getSentPacketsNum(stage1_xchg_) > 10) &&
            ((100. * stats_mgr.getDroppedPacketsNum(stage1_xchg_) /
              stats_mgr.getSentPacketsNum(stage1_xchg_)) >=
             options_.getMaxDropPercentage()[0]))
        {
            max_pdrops = true;
        }
    }
    // Check if we reached maximum drops percentage of ACK/REPLY packets.
    if (options_.getMaxDropPercentage().size() > 1) {
        if ((stats_mgr.getSentPacketsNum(stage2_xchg_) > 10) &&
            ((100. * stats_mgr.getDroppedPacketsNum(stage2_xchg_) /
              stats_mgr.getSentPacketsNum(stage2_xchg_)) >=
             options_.getMaxDropPercentage()[1]))
        {
            max_pdrops = true;
        }
    }
    if (max_pdrops) {
        if (options_.testDiags('e')) {
            std::cout << "Reached maximum percentage of drops." << std::endl;
        }
        if (!tc_.waitToExit()) {
            return true;
        }
    }
    return (false);
}

int
BasicScen::run() {
    StatsMgr& stats_mgr(tc_.getStatsMgr());

    // Preload server with the number of packets.
    if (options_.getPreload() > 0) {
        tc_.sendPackets(options_.getPreload(), true);
    }

    // Fork and run command specified with -w<wrapped-command>
    if (!options_.getWrapped().empty()) {
        tc_.runWrapped();
    }

    tc_.start();

    for (;;) {
        // Calculate number of packets to be sent to stay
        // catch up with rate.
        uint64_t packets_due =
            basic_rate_control_.getOutboundMessageCount(!tc_.exit_time_.is_not_a_date_time());
        if ((packets_due == 0) && options_.testDiags('i')) {
            stats_mgr.incrementCounter("shortwait");
        }

        // Pull some packets from receiver thread, process them, update some stats
        // and respond to the server if needed.
        auto pkt_count = tc_.consumeReceivedPackets();

        // If there is nothing to do in this loop iteration then do some sleep to make
        // CPU idle for a moment, to not consume 100% CPU all the time
        // but only if it is not that high request rate expected.
        if (options_.getRate() < 10000 && packets_due == 0 && pkt_count == 0) {
            /// @todo: need to implement adaptive time here, so the sleep time
            /// is not fixed, but adjusts to current situation.
            usleep(1);
        }

        // If test period finished, maximum number of packet drops
        // has been reached or test has been interrupted we have to
        // finish the test.
        if (checkExitConditions()) {
            break;
        }

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

        // If -f<renew-rate> option was specified we have to check how many
        // Renew packets should be sent to catch up with a desired rate.
        if (options_.getRenewRate() != 0) {
            uint64_t renew_packets_due =
                renew_rate_control_.getOutboundMessageCount(!tc_.exit_time_.is_not_a_date_time());

            // Send multiple renews to satisfy the desired rate.
            if (options_.getIpVersion() == 4) {
                tc_.sendMultipleMessages4(DHCPREQUEST, renew_packets_due);
            } else {
                tc_.sendMultipleMessages6(DHCPV6_RENEW, renew_packets_due);
            }
        }

        // If -F<release-rate> option was specified we have to check how many
        // Release messages should be sent to catch up with a desired rate.
        if (options_.getReleaseRate() != 0) {
            uint64_t release_packets_due =
                release_rate_control_.getOutboundMessageCount(!tc_.exit_time_.is_not_a_date_time());
            // Send Release messages.

            if (options_.getIpVersion() == 4) {
                tc_.sendMultipleMessages4(DHCPRELEASE, release_packets_due);
            } else {
                tc_.sendMultipleMessages6(DHCPV6_RELEASE, release_packets_due);
            }
        }

        // Report delay means that user requested printing number
        // of sent/received/dropped packets repeatedly.
        if (options_.getReportDelay() > 0) {
            tc_.printIntermediateStats();
        }

        // If we are sending Renews to the server, the Reply packets are cached
        // so as leases for which we send Renews can be identified. The major
        // issue with this approach is that most of the time we are caching
        // more packets than we actually need. This function removes excessive
        // Reply messages to reduce the memory and CPU utilization. Note that
        // searches in the long list of Reply packets increases CPU utilization.
        tc_.cleanCachedPackets();
    }

    tc_.stop();

    tc_.printStats();

    if (!options_.getWrapped().empty()) {
        // true means that we execute wrapped command with 'stop' argument.
        tc_.runWrapped(true);
    }

    // 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;
    }
    // Print packet templates. Even if -T options have not been specified the
    // dynamically build packet will be printed if at least one has been sent.
    if (options_.testDiags('T')) {
        tc_.printTemplates();
    }

    // Print any received leases.
    if (options_.testDiags('l')) {
        stats_mgr.printLeases();
    }

    int ret_code = 0;
    // Check if any packet drops occurred.
    ret_code = stats_mgr.droppedPackets() ? 3 : 0;
    return (ret_code);
}

}  // namespace perfdhcp
}  // namespace isc