Line data Source code
1 : /* Copyright (c) 2018-2021, The Tor Project, Inc. */
2 : /* See LICENSE for licensing information */
3 :
4 : /**
5 : * \file test_bwmgt.c
6 : * \brief tests for bandwidth management / token bucket functions
7 : */
8 :
9 : #define CONFIG_PRIVATE
10 : #define CONNECTION_PRIVATE
11 : #define DIRAUTH_SYS_PRIVATE
12 : #define TOKEN_BUCKET_PRIVATE
13 :
14 : #include "core/or/or.h"
15 :
16 : #include "app/config/config.h"
17 : #include "core/mainloop/connection.h"
18 : #include "feature/dirauth/dirauth_sys.h"
19 : #include "feature/dircommon/directory.h"
20 : #include "feature/nodelist/microdesc.h"
21 : #include "feature/nodelist/networkstatus.h"
22 : #include "feature/nodelist/nodelist.h"
23 : #include "feature/nodelist/routerlist.h"
24 : #include "lib/crypt_ops/crypto_rand.h"
25 : #include "lib/evloop/token_bucket.h"
26 : #include "test/test.h"
27 : #include "test/test_helpers.h"
28 :
29 : #include "app/config/or_options_st.h"
30 : #include "core/or/connection_st.h"
31 : #include "feature/dirauth/dirauth_options_st.h"
32 : #include "feature/nodelist/microdesc_st.h"
33 : #include "feature/nodelist/networkstatus_st.h"
34 : #include "feature/nodelist/routerinfo_st.h"
35 : #include "feature/nodelist/routerstatus_st.h"
36 :
37 : // an imaginary time, in timestamp units. Chosen so it will roll over.
38 : static const uint32_t START_TS = UINT32_MAX-10;
39 : static const int32_t KB = 1024;
40 : static const uint32_t GB = (UINT64_C(1) << 30);
41 :
42 : static or_options_t mock_options;
43 :
44 : static const or_options_t *
45 14 : mock_get_options(void)
46 : {
47 14 : return &mock_options;
48 : }
49 :
50 : static networkstatus_t *dummy_ns = NULL;
51 : static networkstatus_t *
52 1 : mock_networkstatus_get_latest_consensus(void)
53 : {
54 1 : return dummy_ns;
55 : }
56 :
57 : static networkstatus_t *
58 1 : mock_networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f)
59 : {
60 1 : tor_assert(f == FLAV_MICRODESC);
61 1 : return dummy_ns;
62 : }
63 :
64 : /* Number of address a single node_t can have. Default to the production
65 : * value. This is to control the size of the bloom filter. */
66 : static int addr_per_node = 2;
67 : static int
68 2 : mock_get_estimated_address_per_node(void)
69 : {
70 2 : return addr_per_node;
71 : }
72 :
73 : static void
74 1 : test_bwmgt_token_buf_init(void *arg)
75 : {
76 1 : (void)arg;
77 1 : token_bucket_rw_t b;
78 :
79 1 : token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
80 : // Burst is correct
81 1 : tt_uint_op(b.cfg.burst, OP_EQ, 64*KB);
82 : // Rate is correct, within 1 percent.
83 : {
84 2 : uint32_t ticks_per_sec =
85 1 : (uint32_t) monotime_msec_to_approx_coarse_stamp_units(1000);
86 1 : uint32_t rate_per_sec = (b.cfg.rate * ticks_per_sec / TICKS_PER_STEP);
87 :
88 1 : tt_uint_op(rate_per_sec, OP_GT, 16*KB-160);
89 1 : tt_uint_op(rate_per_sec, OP_LT, 16*KB+160);
90 : }
91 : // Bucket starts out full:
92 1 : tt_uint_op(b.last_refilled_at_timestamp, OP_EQ, START_TS);
93 1 : tt_int_op(b.read_bucket.bucket, OP_EQ, 64*KB);
94 :
95 1 : done:
96 1 : ;
97 1 : }
98 :
99 : static void
100 1 : test_bwmgt_token_buf_adjust(void *arg)
101 : {
102 1 : (void)arg;
103 1 : token_bucket_rw_t b;
104 :
105 1 : token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
106 :
107 1 : uint32_t rate_orig = b.cfg.rate;
108 : // Increasing burst
109 1 : token_bucket_rw_adjust(&b, 16*KB, 128*KB);
110 1 : tt_uint_op(b.cfg.rate, OP_EQ, rate_orig);
111 1 : tt_uint_op(b.read_bucket.bucket, OP_EQ, 64*KB);
112 1 : tt_uint_op(b.cfg.burst, OP_EQ, 128*KB);
113 :
114 : // Decreasing burst but staying above bucket
115 1 : token_bucket_rw_adjust(&b, 16*KB, 96*KB);
116 1 : tt_uint_op(b.cfg.rate, OP_EQ, rate_orig);
117 1 : tt_uint_op(b.read_bucket.bucket, OP_EQ, 64*KB);
118 1 : tt_uint_op(b.cfg.burst, OP_EQ, 96*KB);
119 :
120 : // Decreasing burst below bucket,
121 1 : token_bucket_rw_adjust(&b, 16*KB, 48*KB);
122 1 : tt_uint_op(b.cfg.rate, OP_EQ, rate_orig);
123 1 : tt_uint_op(b.read_bucket.bucket, OP_EQ, 48*KB);
124 1 : tt_uint_op(b.cfg.burst, OP_EQ, 48*KB);
125 :
126 : // Changing rate.
127 1 : token_bucket_rw_adjust(&b, 32*KB, 48*KB);
128 1 : tt_uint_op(b.cfg.rate, OP_GE, rate_orig*2 - 10);
129 1 : tt_uint_op(b.cfg.rate, OP_LE, rate_orig*2 + 10);
130 1 : tt_uint_op(b.read_bucket.bucket, OP_EQ, 48*KB);
131 1 : tt_uint_op(b.cfg.burst, OP_EQ, 48*KB);
132 :
133 1 : done:
134 1 : ;
135 1 : }
136 :
137 : static void
138 1 : test_bwmgt_token_buf_dec(void *arg)
139 : {
140 1 : (void)arg;
141 1 : token_bucket_rw_t b;
142 1 : token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
143 :
144 : // full-to-not-full.
145 1 : tt_int_op(0, OP_EQ, token_bucket_rw_dec_read(&b, KB));
146 1 : tt_int_op(b.read_bucket.bucket, OP_EQ, 63*KB);
147 :
148 : // Full to almost-not-full
149 1 : tt_int_op(0, OP_EQ, token_bucket_rw_dec_read(&b, 63*KB - 1));
150 1 : tt_int_op(b.read_bucket.bucket, OP_EQ, 1);
151 :
152 : // almost-not-full to empty.
153 1 : tt_int_op(1, OP_EQ, token_bucket_rw_dec_read(&b, 1));
154 1 : tt_int_op(b.read_bucket.bucket, OP_EQ, 0);
155 :
156 : // reset bucket, try full-to-empty
157 1 : token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
158 1 : tt_int_op(1, OP_EQ, token_bucket_rw_dec_read(&b, 64*KB));
159 1 : tt_int_op(b.read_bucket.bucket, OP_EQ, 0);
160 :
161 : // reset bucket, try underflow.
162 1 : token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
163 1 : tt_int_op(1, OP_EQ, token_bucket_rw_dec_read(&b, 64*KB + 1));
164 1 : tt_int_op(b.read_bucket.bucket, OP_EQ, -1);
165 :
166 : // A second underflow does not make the bucket empty.
167 1 : tt_int_op(0, OP_EQ, token_bucket_rw_dec_read(&b, 1000));
168 1 : tt_int_op(b.read_bucket.bucket, OP_EQ, -1001);
169 :
170 1 : done:
171 1 : ;
172 1 : }
173 :
174 : static void
175 1 : test_bwmgt_token_buf_refill(void *arg)
176 : {
177 1 : (void)arg;
178 1 : token_bucket_rw_t b;
179 2 : const uint32_t BW_SEC =
180 1 : (uint32_t)monotime_msec_to_approx_coarse_stamp_units(1000);
181 1 : token_bucket_rw_init(&b, 16*KB, 64*KB, START_TS);
182 :
183 : /* Make the buffer much emptier, then let one second elapse. */
184 1 : token_bucket_rw_dec_read(&b, 48*KB);
185 1 : tt_int_op(b.read_bucket.bucket, OP_EQ, 16*KB);
186 1 : tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC));
187 1 : tt_int_op(b.read_bucket.bucket, OP_GT, 32*KB - 300);
188 1 : tt_int_op(b.read_bucket.bucket, OP_LT, 32*KB + 300);
189 :
190 : /* Another half second. */
191 1 : tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC*3/2));
192 1 : tt_int_op(b.read_bucket.bucket, OP_GT, 40*KB - 400);
193 1 : tt_int_op(b.read_bucket.bucket, OP_LT, 40*KB + 400);
194 1 : tt_uint_op(b.last_refilled_at_timestamp, OP_EQ, START_TS + BW_SEC*3/2);
195 :
196 : /* No time: nothing happens. */
197 : {
198 1 : const uint32_t bucket_orig = b.read_bucket.bucket;
199 1 : tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC*3/2));
200 1 : tt_int_op(b.read_bucket.bucket, OP_EQ, bucket_orig);
201 : }
202 :
203 : /* Another 30 seconds: fill the bucket. */
204 1 : tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b,
205 : START_TS + BW_SEC*3/2 + BW_SEC*30));
206 1 : tt_int_op(b.read_bucket.bucket, OP_EQ, b.cfg.burst);
207 1 : tt_uint_op(b.last_refilled_at_timestamp, OP_EQ,
208 : START_TS + BW_SEC*3/2 + BW_SEC*30);
209 :
210 : /* Another 30 seconds: nothing happens. */
211 1 : tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b,
212 : START_TS + BW_SEC*3/2 + BW_SEC*60));
213 1 : tt_int_op(b.read_bucket.bucket, OP_EQ, b.cfg.burst);
214 1 : tt_uint_op(b.last_refilled_at_timestamp, OP_EQ,
215 : START_TS + BW_SEC*3/2 + BW_SEC*60);
216 :
217 : /* Empty the bucket, let two seconds pass, and make sure that a refill is
218 : * noticed. */
219 1 : tt_int_op(1, OP_EQ, token_bucket_rw_dec_read(&b, b.cfg.burst));
220 1 : tt_int_op(0, OP_EQ, b.read_bucket.bucket);
221 1 : tt_int_op(1, OP_EQ, token_bucket_rw_refill(&b,
222 : START_TS + BW_SEC*3/2 + BW_SEC*61));
223 1 : tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b,
224 : START_TS + BW_SEC*3/2 + BW_SEC*62));
225 1 : tt_int_op(b.read_bucket.bucket, OP_GT, 32*KB-400);
226 1 : tt_int_op(b.read_bucket.bucket, OP_LT, 32*KB+400);
227 :
228 : /* Underflow the bucket, make sure we detect when it has tokens again. */
229 1 : tt_int_op(1, OP_EQ,
230 : token_bucket_rw_dec_read(&b, b.read_bucket.bucket+16*KB));
231 1 : tt_int_op(-16*KB, OP_EQ, b.read_bucket.bucket);
232 : // half a second passes...
233 1 : tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC*64));
234 1 : tt_int_op(b.read_bucket.bucket, OP_GT, -8*KB-300);
235 1 : tt_int_op(b.read_bucket.bucket, OP_LT, -8*KB+300);
236 : // a second passes
237 1 : tt_int_op(1, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC*65));
238 1 : tt_int_op(b.read_bucket.bucket, OP_GT, 8*KB-400);
239 1 : tt_int_op(b.read_bucket.bucket, OP_LT, 8*KB+400);
240 :
241 : // We step a second backwards, and nothing happens.
242 1 : tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, START_TS + BW_SEC*64));
243 1 : tt_int_op(b.read_bucket.bucket, OP_GT, 8*KB-400);
244 1 : tt_int_op(b.read_bucket.bucket, OP_LT, 8*KB+400);
245 :
246 : // A ridiculous amount of time passes.
247 1 : tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, INT32_MAX));
248 1 : tt_int_op(b.read_bucket.bucket, OP_EQ, b.cfg.burst);
249 :
250 1 : done:
251 1 : ;
252 1 : }
253 :
254 : /* Test some helper functions we use within the token bucket interface. */
255 : static void
256 1 : test_bwmgt_token_buf_helpers(void *arg)
257 : {
258 1 : uint32_t ret;
259 :
260 1 : (void) arg;
261 :
262 : /* The returned value will be OS specific but in any case, it should be
263 : * greater than 1 since we are passing 1GB/sec rate. */
264 1 : ret = rate_per_sec_to_rate_per_step(1 * GB);
265 1 : tt_u64_op(ret, OP_GT, 1);
266 :
267 : /* We default to 1 in case rate is 0. */
268 1 : ret = rate_per_sec_to_rate_per_step(0);
269 1 : tt_u64_op(ret, OP_EQ, 1);
270 :
271 1 : done:
272 1 : ;
273 1 : }
274 :
275 : static void
276 1 : test_bwmgt_dir_conn_global_write_low(void *arg)
277 : {
278 1 : bool ret;
279 1 : int addr_family;
280 1 : connection_t *conn = NULL;
281 1 : routerstatus_t *rs = NULL; microdesc_t *md = NULL; routerinfo_t *ri = NULL;
282 1 : tor_addr_t relay_addr;
283 1 : dirauth_options_t *dirauth_opts = NULL;
284 :
285 1 : (void) arg;
286 :
287 1 : memset(&mock_options, 0, sizeof(or_options_t));
288 1 : MOCK(networkstatus_get_latest_consensus,
289 : mock_networkstatus_get_latest_consensus);
290 1 : MOCK(networkstatus_get_latest_consensus_by_flavor,
291 : mock_networkstatus_get_latest_consensus_by_flavor);
292 1 : MOCK(get_estimated_address_per_node,
293 : mock_get_estimated_address_per_node);
294 :
295 : /*
296 : * The following is rather complex but that is what it takes to add a dummy
297 : * consensus with a valid routerlist which will populate our node address
298 : * set that we need to lookup to test the known relay code path.
299 : *
300 : * We MUST do that before we MOCK(get_options) else it is another world of
301 : * complexity.
302 : */
303 :
304 : /* This will be the address of our relay. */
305 1 : tor_addr_parse(&relay_addr, "1.2.3.4");
306 :
307 : /* We'll now add a relay into our routerlist and see if we let it. */
308 1 : dummy_ns = tor_malloc_zero(sizeof(*dummy_ns));
309 1 : dummy_ns->flavor = FLAV_MICRODESC;
310 1 : dummy_ns->routerstatus_list = smartlist_new();
311 :
312 1 : md = tor_malloc_zero(sizeof(*md));
313 1 : ri = tor_malloc_zero(sizeof(*ri));
314 1 : rs = tor_malloc_zero(sizeof(*rs));
315 1 : crypto_rand(rs->identity_digest, sizeof(rs->identity_digest));
316 1 : crypto_rand(md->digest, sizeof(md->digest));
317 1 : memcpy(rs->descriptor_digest, md->digest, DIGEST256_LEN);
318 :
319 : /* Set IP address. */
320 1 : tor_addr_copy(&rs->ipv4_addr, &relay_addr);
321 1 : tor_addr_copy(&ri->ipv4_addr, &rs->ipv4_addr);
322 : /* Add the rs to the consensus becoming a node_t. */
323 1 : smartlist_add(dummy_ns->routerstatus_list, rs);
324 :
325 : /* Add all configured authorities (hardcoded) before we set the consensus so
326 : * the address set exists. */
327 1 : ret = consider_adding_dir_servers(&mock_options, &mock_options);
328 1 : tt_int_op(ret, OP_EQ, 0);
329 :
330 : /* This will make the nodelist bloom filter very large
331 : * (the_nodelist->node_addrs) so we will fail the contain test rarely. */
332 1 : addr_per_node = 1024;
333 :
334 1 : nodelist_set_consensus(dummy_ns);
335 :
336 1 : dirauth_opts = tor_malloc_zero(sizeof(dirauth_options_t));
337 1 : dirauth_opts->AuthDirRejectRequestsUnderLoad = 0;
338 1 : dirauth_set_options(dirauth_opts);
339 :
340 : /* Ok, now time to control which options we use. */
341 1 : MOCK(get_options, mock_get_options);
342 :
343 : /* Set ourselves as an authoritative dir. */
344 1 : mock_options.AuthoritativeDir = 1;
345 1 : mock_options.V3AuthoritativeDir = 1;
346 1 : mock_options.UseDefaultFallbackDirs = 0;
347 :
348 : /* This will set our global bucket to 1 byte and thus we will hit the
349 : * banwdith limit in our test. */
350 1 : mock_options.BandwidthRate = 1;
351 1 : mock_options.BandwidthBurst = 1;
352 :
353 : /* Else an IPv4 address screams. */
354 1 : mock_options.ClientUseIPv4 = 1;
355 1 : mock_options.ClientUseIPv6 = 1;
356 :
357 : /* Initialize the global buckets. */
358 1 : connection_bucket_init();
359 :
360 : /* The address "127.0.0.1" is set with this helper. */
361 1 : conn = test_conn_get_connection(DIR_CONN_STATE_MIN_, CONN_TYPE_DIR,
362 : DIR_PURPOSE_MIN_);
363 1 : tt_assert(conn);
364 :
365 : /* First try a non authority non relay IP thus a client but we are not
366 : * configured to reject requests under load so we should get a false value
367 : * that our limit is _not_ low. */
368 1 : addr_family = tor_addr_parse(&conn->addr, "1.1.1.1");
369 1 : tt_int_op(addr_family, OP_EQ, AF_INET);
370 1 : ret = connection_dir_is_global_write_low(conn, INT_MAX);
371 1 : tt_int_op(ret, OP_EQ, 0);
372 :
373 : /* Now, we will reject requests under load so try again a non authority non
374 : * relay IP thus a client. We should get a warning that our limit is too
375 : * low. */
376 1 : dirauth_opts->AuthDirRejectRequestsUnderLoad = 1;
377 :
378 1 : addr_family = tor_addr_parse(&conn->addr, "1.1.1.1");
379 1 : tt_int_op(addr_family, OP_EQ, AF_INET);
380 1 : ret = connection_dir_is_global_write_low(conn, INT_MAX);
381 1 : tt_int_op(ret, OP_EQ, 1);
382 :
383 : /* Now, lets try with a connection address from moria1. It should always
384 : * pass even though our limit is too low. */
385 1 : addr_family = tor_addr_parse(&conn->addr, "128.31.0.39");
386 1 : tt_int_op(addr_family, OP_EQ, AF_INET);
387 1 : ret = connection_dir_is_global_write_low(conn, INT_MAX);
388 1 : tt_int_op(ret, OP_EQ, 0);
389 :
390 : /* IPv6 testing of gabelmoo. */
391 1 : addr_family = tor_addr_parse(&conn->addr, "[2001:638:a000:4140::ffff:189]");
392 1 : tt_int_op(addr_family, OP_EQ, AF_INET6);
393 1 : ret = connection_dir_is_global_write_low(conn, INT_MAX);
394 1 : tt_int_op(ret, OP_EQ, 0);
395 :
396 : /* Lets retry with a known relay address. It should pass. Possible due to
397 : * our consensus setting above. */
398 1 : memcpy(&conn->addr, &relay_addr, sizeof(tor_addr_t));
399 1 : ret = connection_dir_is_global_write_low(conn, INT_MAX);
400 1 : tt_int_op(ret, OP_EQ, 0);
401 :
402 : /* Lets retry with a random IP that is not an authority nor a relay. */
403 1 : addr_family = tor_addr_parse(&conn->addr, "1.2.3.4");
404 1 : tt_int_op(addr_family, OP_EQ, AF_INET);
405 1 : ret = connection_dir_is_global_write_low(conn, INT_MAX);
406 1 : tt_int_op(ret, OP_EQ, 0);
407 :
408 : /* Finally, just make sure it still denies an IP if we are _not_ a v3
409 : * directory authority. */
410 1 : mock_options.V3AuthoritativeDir = 0;
411 1 : addr_family = tor_addr_parse(&conn->addr, "1.2.3.4");
412 1 : tt_int_op(addr_family, OP_EQ, AF_INET);
413 1 : ret = connection_dir_is_global_write_low(conn, INT_MAX);
414 1 : tt_int_op(ret, OP_EQ, 1);
415 :
416 : /* Random IPv6 should not be allowed. */
417 1 : addr_family = tor_addr_parse(&conn->addr, "[CAFE::ACAB]");
418 1 : tt_int_op(addr_family, OP_EQ, AF_INET6);
419 1 : ret = connection_dir_is_global_write_low(conn, INT_MAX);
420 1 : tt_int_op(ret, OP_EQ, 1);
421 :
422 1 : done:
423 1 : connection_free_minimal(conn);
424 1 : routerstatus_free(rs); routerinfo_free(ri); microdesc_free(md);
425 1 : smartlist_clear(dummy_ns->routerstatus_list);
426 1 : networkstatus_vote_free(dummy_ns);
427 :
428 1 : UNMOCK(get_estimated_address_per_node);
429 1 : UNMOCK(networkstatus_get_latest_consensus);
430 1 : UNMOCK(networkstatus_get_latest_consensus_by_flavor);
431 1 : UNMOCK(get_options);
432 1 : }
433 :
434 : #define BWMGT(name) \
435 : { #name, test_bwmgt_ ## name , TT_FORK, NULL, NULL }
436 :
437 : struct testcase_t bwmgt_tests[] = {
438 : BWMGT(token_buf_init),
439 : BWMGT(token_buf_adjust),
440 : BWMGT(token_buf_dec),
441 : BWMGT(token_buf_refill),
442 : BWMGT(token_buf_helpers),
443 :
444 : BWMGT(dir_conn_global_write_low),
445 : END_OF_TESTCASES
446 : };
|