1*eabc0478Schristos /* $NetBSD: test-ratelim.c,v 1.8 2024/08/18 20:47:23 christos Exp $ */ 28585484eSchristos 38585484eSchristos /* 48585484eSchristos * Copyright (c) 2009-2012 Niels Provos and Nick Mathewson 58585484eSchristos * 68585484eSchristos * Redistribution and use in source and binary forms, with or without 78585484eSchristos * modification, are permitted provided that the following conditions 88585484eSchristos * are met: 98585484eSchristos * 1. Redistributions of source code must retain the above copyright 108585484eSchristos * notice, this list of conditions and the following disclaimer. 118585484eSchristos * 2. Redistributions in binary form must reproduce the above copyright 128585484eSchristos * notice, this list of conditions and the following disclaimer in the 138585484eSchristos * documentation and/or other materials provided with the distribution. 148585484eSchristos * 3. The name of the author may not be used to endorse or promote products 158585484eSchristos * derived from this software without specific prior written permission. 168585484eSchristos * 178585484eSchristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 188585484eSchristos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 198585484eSchristos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 208585484eSchristos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 218585484eSchristos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 228585484eSchristos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 238585484eSchristos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 248585484eSchristos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 258585484eSchristos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 268585484eSchristos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 278585484eSchristos */ 288585484eSchristos #include "../util-internal.h" 298585484eSchristos 308585484eSchristos #include <stdio.h> 318585484eSchristos #include <stdlib.h> 328585484eSchristos #include <string.h> 338585484eSchristos #include <assert.h> 348585484eSchristos #include <math.h> 358585484eSchristos 368585484eSchristos #ifdef _WIN32 378585484eSchristos #include <winsock2.h> 388585484eSchristos #include <ws2tcpip.h> 398585484eSchristos #else 408585484eSchristos #include <sys/socket.h> 418585484eSchristos #include <netinet/in.h> 428585484eSchristos # ifdef _XOPEN_SOURCE_EXTENDED 438585484eSchristos # include <arpa/inet.h> 448585484eSchristos # endif 458585484eSchristos #endif 468585484eSchristos #include <signal.h> 478585484eSchristos 488585484eSchristos #include "event2/bufferevent.h" 498585484eSchristos #include "event2/buffer.h" 508585484eSchristos #include "event2/event.h" 518585484eSchristos #include "event2/util.h" 528585484eSchristos #include "event2/listener.h" 538585484eSchristos #include "event2/thread.h" 548585484eSchristos 55*eabc0478Schristos #ifndef MIN 56*eabc0478Schristos #define MIN(a,b) (((a)<(b))?(a):(b)) 57*eabc0478Schristos #endif 58*eabc0478Schristos 597476e6e4Schristos static struct evutil_weakrand_state weakrand_state; 608585484eSchristos 618585484eSchristos static int cfg_verbose = 0; 628585484eSchristos static int cfg_help = 0; 638585484eSchristos 648585484eSchristos static int cfg_n_connections = 30; 658585484eSchristos static int cfg_duration = 5; 668585484eSchristos static int cfg_connlimit = 0; 678585484eSchristos static int cfg_grouplimit = 0; 688585484eSchristos static int cfg_tick_msec = 1000; 698585484eSchristos static int cfg_min_share = -1; 708585484eSchristos static int cfg_group_drain = 0; 718585484eSchristos 728585484eSchristos static int cfg_connlimit_tolerance = -1; 738585484eSchristos static int cfg_grouplimit_tolerance = -1; 748585484eSchristos static int cfg_stddev_tolerance = -1; 758585484eSchristos 768585484eSchristos #ifdef _WIN32 778585484eSchristos static int cfg_enable_iocp = 0; 788585484eSchristos #endif 798585484eSchristos 808585484eSchristos static struct timeval cfg_tick = { 0, 500*1000 }; 818585484eSchristos 828585484eSchristos static struct ev_token_bucket_cfg *conn_bucket_cfg = NULL; 838585484eSchristos static struct ev_token_bucket_cfg *group_bucket_cfg = NULL; 848585484eSchristos struct bufferevent_rate_limit_group *ratelim_group = NULL; 858585484eSchristos static double seconds_per_tick = 0.0; 868585484eSchristos 878585484eSchristos struct client_state { 888585484eSchristos size_t queued; 898585484eSchristos ev_uint64_t received; 908585484eSchristos 918585484eSchristos }; 928585484eSchristos static const struct timeval *ms100_common=NULL; 938585484eSchristos 94*eabc0478Schristos /* Timers bias for slow CPUs, affects: 95*eabc0478Schristos * - cfg_connlimit_tolerance (--check-connlimit) 96*eabc0478Schristos * - cfg_grouplimit_tolerance (--check-grouplimit) 97*eabc0478Schristos * - cfg_stddev_tolerance (--check-stddev) 98*eabc0478Schristos */ 99*eabc0478Schristos static int timer_bias_events; 100*eabc0478Schristos static struct timeval timer_bias_start; 101*eabc0478Schristos double timer_bias_spend; 102*eabc0478Schristos /* Real cost is less (approximately ~5 usec), 103*eabc0478Schristos * this macros adjusted to make the bias less */ 104*eabc0478Schristos #define TIMER_MAX_COST_USEC 10 105*eabc0478Schristos 1068585484eSchristos /* info from check_bucket_levels_cb */ 1078585484eSchristos static int total_n_bev_checks = 0; 1088585484eSchristos static ev_int64_t total_rbucket_level=0; 1098585484eSchristos static ev_int64_t total_wbucket_level=0; 1108585484eSchristos static ev_int64_t total_max_to_read=0; 1118585484eSchristos static ev_int64_t total_max_to_write=0; 1128585484eSchristos static ev_int64_t max_bucket_level=EV_INT64_MIN; 1138585484eSchristos static ev_int64_t min_bucket_level=EV_INT64_MAX; 1148585484eSchristos 1158585484eSchristos /* from check_group_bucket_levels_cb */ 1168585484eSchristos static int total_n_group_bev_checks = 0; 1178585484eSchristos static ev_int64_t total_group_rbucket_level = 0; 1188585484eSchristos static ev_int64_t total_group_wbucket_level = 0; 1198585484eSchristos 1208585484eSchristos static int n_echo_conns_open = 0; 1218585484eSchristos 1228585484eSchristos /* Info on the open connections */ 1238585484eSchristos struct bufferevent **bevs; 1248585484eSchristos struct client_state *states; 1258585484eSchristos struct bufferevent_rate_limit_group *group = NULL; 1268585484eSchristos 1278585484eSchristos static void check_bucket_levels_cb(evutil_socket_t fd, short events, void *arg); 1288585484eSchristos 1298585484eSchristos static void 1308585484eSchristos loud_writecb(struct bufferevent *bev, void *ctx) 1318585484eSchristos { 1328585484eSchristos struct client_state *cs = ctx; 1338585484eSchristos struct evbuffer *output = bufferevent_get_output(bev); 1348585484eSchristos char buf[1024]; 1357476e6e4Schristos int r = evutil_weakrand_(&weakrand_state); 1368585484eSchristos memset(buf, r, sizeof(buf)); 1378585484eSchristos while (evbuffer_get_length(output) < 8192) { 1388585484eSchristos evbuffer_add(output, buf, sizeof(buf)); 1398585484eSchristos cs->queued += sizeof(buf); 1408585484eSchristos } 1418585484eSchristos } 1428585484eSchristos 1438585484eSchristos static void 1448585484eSchristos discard_readcb(struct bufferevent *bev, void *ctx) 1458585484eSchristos { 1468585484eSchristos struct client_state *cs = ctx; 1478585484eSchristos struct evbuffer *input = bufferevent_get_input(bev); 1488585484eSchristos size_t len = evbuffer_get_length(input); 1498585484eSchristos evbuffer_drain(input, len); 1508585484eSchristos cs->received += len; 1518585484eSchristos } 1528585484eSchristos 1538585484eSchristos static void 1548585484eSchristos write_on_connectedcb(struct bufferevent *bev, short what, void *ctx) 1558585484eSchristos { 1568585484eSchristos if (what & BEV_EVENT_CONNECTED) { 1578585484eSchristos loud_writecb(bev, ctx); 1588585484eSchristos /* XXXX this shouldn't be needed. */ 1598585484eSchristos bufferevent_enable(bev, EV_READ|EV_WRITE); 1608585484eSchristos } 1618585484eSchristos } 1628585484eSchristos 1638585484eSchristos static void 1648585484eSchristos echo_readcb(struct bufferevent *bev, void *ctx) 1658585484eSchristos { 1668585484eSchristos struct evbuffer *input = bufferevent_get_input(bev); 1678585484eSchristos struct evbuffer *output = bufferevent_get_output(bev); 1688585484eSchristos 1698585484eSchristos evbuffer_add_buffer(output, input); 1708585484eSchristos if (evbuffer_get_length(output) > 1024000) 1718585484eSchristos bufferevent_disable(bev, EV_READ); 1728585484eSchristos } 1738585484eSchristos 1748585484eSchristos static void 1758585484eSchristos echo_writecb(struct bufferevent *bev, void *ctx) 1768585484eSchristos { 1778585484eSchristos struct evbuffer *output = bufferevent_get_output(bev); 1788585484eSchristos if (evbuffer_get_length(output) < 512000) 1798585484eSchristos bufferevent_enable(bev, EV_READ); 1808585484eSchristos } 1818585484eSchristos 1828585484eSchristos static void 1838585484eSchristos echo_eventcb(struct bufferevent *bev, short what, void *ctx) 1848585484eSchristos { 1858585484eSchristos if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { 1868585484eSchristos --n_echo_conns_open; 1878585484eSchristos bufferevent_free(bev); 1888585484eSchristos } 1898585484eSchristos } 1908585484eSchristos 1918585484eSchristos static void 1928585484eSchristos echo_listenercb(struct evconnlistener *listener, evutil_socket_t newsock, 1938585484eSchristos struct sockaddr *sourceaddr, int socklen, void *ctx) 1948585484eSchristos { 1958585484eSchristos struct event_base *base = ctx; 1968585484eSchristos int flags = BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE; 1978585484eSchristos struct bufferevent *bev; 1988585484eSchristos 1998585484eSchristos bev = bufferevent_socket_new(base, newsock, flags); 2008585484eSchristos bufferevent_setcb(bev, echo_readcb, echo_writecb, echo_eventcb, NULL); 2018585484eSchristos if (conn_bucket_cfg) { 2028585484eSchristos struct event *check_event = 2038585484eSchristos event_new(base, -1, EV_PERSIST, check_bucket_levels_cb, bev); 2048585484eSchristos bufferevent_set_rate_limit(bev, conn_bucket_cfg); 205b8ecfcfeSchristos 206b8ecfcfeSchristos assert(bufferevent_get_token_bucket_cfg(bev) != NULL); 2078585484eSchristos event_add(check_event, ms100_common); 2088585484eSchristos } 2098585484eSchristos if (ratelim_group) 2108585484eSchristos bufferevent_add_to_rate_limit_group(bev, ratelim_group); 2118585484eSchristos ++n_echo_conns_open; 2128585484eSchristos bufferevent_enable(bev, EV_READ|EV_WRITE); 2138585484eSchristos } 2148585484eSchristos 2158585484eSchristos /* Called periodically to check up on how full the buckets are */ 2168585484eSchristos static void 2178585484eSchristos check_bucket_levels_cb(evutil_socket_t fd, short events, void *arg) 2188585484eSchristos { 2198585484eSchristos struct bufferevent *bev = arg; 2208585484eSchristos 2218585484eSchristos ev_ssize_t r = bufferevent_get_read_limit(bev); 2228585484eSchristos ev_ssize_t w = bufferevent_get_write_limit(bev); 2238585484eSchristos ev_ssize_t rm = bufferevent_get_max_to_read(bev); 2248585484eSchristos ev_ssize_t wm = bufferevent_get_max_to_write(bev); 2258585484eSchristos /* XXXX check that no value is above the cofigured burst 2268585484eSchristos * limit */ 2278585484eSchristos total_rbucket_level += r; 2288585484eSchristos total_wbucket_level += w; 2298585484eSchristos total_max_to_read += rm; 2308585484eSchristos total_max_to_write += wm; 2318585484eSchristos #define B(x) \ 2328585484eSchristos if ((x) > max_bucket_level) \ 2338585484eSchristos max_bucket_level = (x); \ 2348585484eSchristos if ((x) < min_bucket_level) \ 2358585484eSchristos min_bucket_level = (x) 2368585484eSchristos B(r); 2378585484eSchristos B(w); 2388585484eSchristos #undef B 2398585484eSchristos 2408585484eSchristos total_n_bev_checks++; 2417476e6e4Schristos if (total_n_bev_checks >= .8 * ((double)cfg_duration / cfg_tick_msec) * cfg_n_connections) { 2428585484eSchristos event_free(event_base_get_running_event(bufferevent_get_base(bev))); 2438585484eSchristos } 2448585484eSchristos } 2458585484eSchristos 2468585484eSchristos static void 2478585484eSchristos check_group_bucket_levels_cb(evutil_socket_t fd, short events, void *arg) 2488585484eSchristos { 2498585484eSchristos if (ratelim_group) { 2508585484eSchristos ev_ssize_t r = bufferevent_rate_limit_group_get_read_limit(ratelim_group); 2518585484eSchristos ev_ssize_t w = bufferevent_rate_limit_group_get_write_limit(ratelim_group); 2528585484eSchristos total_group_rbucket_level += r; 2538585484eSchristos total_group_wbucket_level += w; 2548585484eSchristos } 2558585484eSchristos ++total_n_group_bev_checks; 2568585484eSchristos } 2578585484eSchristos 2588585484eSchristos static void 2598585484eSchristos group_drain_cb(evutil_socket_t fd, short events, void *arg) 2608585484eSchristos { 2618585484eSchristos bufferevent_rate_limit_group_decrement_read(ratelim_group, cfg_group_drain); 2628585484eSchristos bufferevent_rate_limit_group_decrement_write(ratelim_group, cfg_group_drain); 2638585484eSchristos } 2648585484eSchristos 265*eabc0478Schristos static void 266*eabc0478Schristos timer_bias_cb(evutil_socket_t fd, short events, void *arg) 267*eabc0478Schristos { 268*eabc0478Schristos struct event *event = arg; 269*eabc0478Schristos struct timeval end; 270*eabc0478Schristos struct timeval diff; 271*eabc0478Schristos 272*eabc0478Schristos /** XXX: use rdtsc? (portability issues?) */ 273*eabc0478Schristos evutil_gettimeofday(&end, NULL); 274*eabc0478Schristos evutil_timersub(&end, &timer_bias_start, &diff); 275*eabc0478Schristos timer_bias_spend += diff.tv_sec + diff.tv_usec * 1e6; 276*eabc0478Schristos timer_bias_start = end; 277*eabc0478Schristos 278*eabc0478Schristos if (++timer_bias_events == 100) 279*eabc0478Schristos event_del(event); 280*eabc0478Schristos } 281*eabc0478Schristos static double 282*eabc0478Schristos timer_bias_calculate(void) 283*eabc0478Schristos { 284*eabc0478Schristos struct event_config *cfg = NULL; 285*eabc0478Schristos struct event_base *base = NULL; 286*eabc0478Schristos struct event *timer = NULL; 287*eabc0478Schristos struct timeval tv = { 0, 1 }; 288*eabc0478Schristos int done = 0; 289*eabc0478Schristos 290*eabc0478Schristos cfg = event_config_new(); 291*eabc0478Schristos if (!cfg) 292*eabc0478Schristos goto err; 293*eabc0478Schristos if (event_config_set_flag(cfg, EVENT_BASE_FLAG_PRECISE_TIMER)) 294*eabc0478Schristos goto err; 295*eabc0478Schristos base = event_base_new_with_config(cfg); 296*eabc0478Schristos if (!base) 297*eabc0478Schristos goto err; 298*eabc0478Schristos 299*eabc0478Schristos timer = event_new(base, -1, EV_PERSIST, timer_bias_cb, event_self_cbarg()); 300*eabc0478Schristos if (!timer || event_add(timer, &tv)) { 301*eabc0478Schristos goto err; 302*eabc0478Schristos } 303*eabc0478Schristos 304*eabc0478Schristos evutil_gettimeofday(&timer_bias_start, NULL); 305*eabc0478Schristos event_base_dispatch(base); 306*eabc0478Schristos done = 1; 307*eabc0478Schristos 308*eabc0478Schristos err: 309*eabc0478Schristos if (cfg) 310*eabc0478Schristos event_config_free(cfg); 311*eabc0478Schristos if (timer) 312*eabc0478Schristos event_free(timer); 313*eabc0478Schristos if (base) 314*eabc0478Schristos event_base_free(base); 315*eabc0478Schristos 316*eabc0478Schristos if (done) 317*eabc0478Schristos return MIN(timer_bias_spend / 1e6 / timer_bias_events / TIMER_MAX_COST_USEC, 5); 318*eabc0478Schristos 319*eabc0478Schristos fprintf(stderr, "Couldn't create event for CPU cycle counter bias\n"); 320*eabc0478Schristos return -1; 321*eabc0478Schristos } 322*eabc0478Schristos 3238585484eSchristos static int 3248585484eSchristos test_ratelimiting(void) 3258585484eSchristos { 3268585484eSchristos struct event_base *base; 3278585484eSchristos struct sockaddr_in sin; 3288585484eSchristos struct evconnlistener *listener; 3298585484eSchristos 3308585484eSchristos struct sockaddr_storage ss; 3318585484eSchristos ev_socklen_t slen; 3328585484eSchristos 3338585484eSchristos int i; 3348585484eSchristos 3358585484eSchristos struct timeval tv; 3368585484eSchristos 3378585484eSchristos ev_uint64_t total_received; 3388585484eSchristos double total_sq_persec, total_persec; 3398585484eSchristos double variance; 3408585484eSchristos double expected_total_persec = -1.0, expected_avg_persec = -1.0; 3418585484eSchristos int ok = 1; 3428585484eSchristos struct event_config *base_cfg; 3438585484eSchristos struct event *periodic_level_check; 3448585484eSchristos struct event *group_drain_event=NULL; 345*eabc0478Schristos double timer_bias; 3468585484eSchristos 3478585484eSchristos memset(&sin, 0, sizeof(sin)); 3488585484eSchristos sin.sin_family = AF_INET; 3498585484eSchristos sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ 3508585484eSchristos sin.sin_port = 0; /* unspecified port */ 3518585484eSchristos 3528585484eSchristos if (0) 3538585484eSchristos event_enable_debug_mode(); 3548585484eSchristos 355*eabc0478Schristos timer_bias = timer_bias_calculate(); 356*eabc0478Schristos if (timer_bias > 1) { 357*eabc0478Schristos fprintf(stderr, "CPU is slow, timers bias is %f\n", timer_bias); 358*eabc0478Schristos cfg_connlimit_tolerance *= timer_bias; 359*eabc0478Schristos cfg_grouplimit_tolerance *= timer_bias; 360*eabc0478Schristos cfg_stddev_tolerance *= timer_bias; 361*eabc0478Schristos } else { 362*eabc0478Schristos printf("CPU is fast enough, timers bias is %f\n", timer_bias); 363*eabc0478Schristos } 364*eabc0478Schristos 3658585484eSchristos base_cfg = event_config_new(); 3668585484eSchristos 3678585484eSchristos #ifdef _WIN32 3688585484eSchristos if (cfg_enable_iocp) { 369*eabc0478Schristos #ifdef EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED 3708585484eSchristos evthread_use_windows_threads(); 371*eabc0478Schristos #endif 3728585484eSchristos event_config_set_flag(base_cfg, EVENT_BASE_FLAG_STARTUP_IOCP); 3738585484eSchristos } 3748585484eSchristos #endif 3758585484eSchristos 3768585484eSchristos base = event_base_new_with_config(base_cfg); 3778585484eSchristos event_config_free(base_cfg); 3788585484eSchristos if (! base) { 3798585484eSchristos fprintf(stderr, "Couldn't create event_base"); 3808585484eSchristos return 1; 3818585484eSchristos } 3828585484eSchristos 3838585484eSchristos listener = evconnlistener_new_bind(base, echo_listenercb, base, 3848585484eSchristos LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1, 3858585484eSchristos (struct sockaddr *)&sin, sizeof(sin)); 3868585484eSchristos if (! listener) { 3878585484eSchristos fprintf(stderr, "Couldn't create listener"); 3888585484eSchristos return 1; 3898585484eSchristos } 3908585484eSchristos 3918585484eSchristos slen = sizeof(ss); 3928585484eSchristos if (getsockname(evconnlistener_get_fd(listener), (struct sockaddr *)&ss, 3938585484eSchristos &slen) < 0) { 3948585484eSchristos perror("getsockname"); 3958585484eSchristos return 1; 3968585484eSchristos } 3978585484eSchristos 3988585484eSchristos if (cfg_connlimit > 0) { 3998585484eSchristos conn_bucket_cfg = ev_token_bucket_cfg_new( 4008585484eSchristos cfg_connlimit, cfg_connlimit * 4, 4018585484eSchristos cfg_connlimit, cfg_connlimit * 4, 4028585484eSchristos &cfg_tick); 4038585484eSchristos assert(conn_bucket_cfg); 4048585484eSchristos } 4058585484eSchristos 4068585484eSchristos if (cfg_grouplimit > 0) { 4078585484eSchristos group_bucket_cfg = ev_token_bucket_cfg_new( 4088585484eSchristos cfg_grouplimit, cfg_grouplimit * 4, 4098585484eSchristos cfg_grouplimit, cfg_grouplimit * 4, 4108585484eSchristos &cfg_tick); 4118585484eSchristos group = ratelim_group = bufferevent_rate_limit_group_new( 4128585484eSchristos base, group_bucket_cfg); 4138585484eSchristos expected_total_persec = cfg_grouplimit - (cfg_group_drain / seconds_per_tick); 4148585484eSchristos expected_avg_persec = cfg_grouplimit / cfg_n_connections; 4158585484eSchristos if (cfg_connlimit > 0 && expected_avg_persec > cfg_connlimit) 4168585484eSchristos expected_avg_persec = cfg_connlimit; 4178585484eSchristos if (cfg_min_share >= 0) 4188585484eSchristos bufferevent_rate_limit_group_set_min_share( 4198585484eSchristos ratelim_group, cfg_min_share); 4208585484eSchristos } 4218585484eSchristos 4228585484eSchristos if (expected_avg_persec < 0 && cfg_connlimit > 0) 4238585484eSchristos expected_avg_persec = cfg_connlimit; 4248585484eSchristos 4258585484eSchristos if (expected_avg_persec > 0) 4268585484eSchristos expected_avg_persec /= seconds_per_tick; 4278585484eSchristos if (expected_total_persec > 0) 4288585484eSchristos expected_total_persec /= seconds_per_tick; 4298585484eSchristos 4308585484eSchristos bevs = calloc(cfg_n_connections, sizeof(struct bufferevent *)); 4318585484eSchristos states = calloc(cfg_n_connections, sizeof(struct client_state)); 4328585484eSchristos 4338585484eSchristos for (i = 0; i < cfg_n_connections; ++i) { 4348585484eSchristos bevs[i] = bufferevent_socket_new(base, -1, 4358585484eSchristos BEV_OPT_CLOSE_ON_FREE|BEV_OPT_THREADSAFE); 4368585484eSchristos assert(bevs[i]); 4378585484eSchristos bufferevent_setcb(bevs[i], discard_readcb, loud_writecb, 4388585484eSchristos write_on_connectedcb, &states[i]); 4398585484eSchristos bufferevent_enable(bevs[i], EV_READ|EV_WRITE); 4408585484eSchristos bufferevent_socket_connect(bevs[i], (struct sockaddr *)&ss, 4418585484eSchristos slen); 4428585484eSchristos } 4438585484eSchristos 4448585484eSchristos tv.tv_sec = cfg_duration - 1; 4458585484eSchristos tv.tv_usec = 995000; 4468585484eSchristos 4478585484eSchristos event_base_loopexit(base, &tv); 4488585484eSchristos 4498585484eSchristos tv.tv_sec = 0; 4508585484eSchristos tv.tv_usec = 100*1000; 4518585484eSchristos ms100_common = event_base_init_common_timeout(base, &tv); 4528585484eSchristos 4538585484eSchristos periodic_level_check = event_new(base, -1, EV_PERSIST, check_group_bucket_levels_cb, NULL); 4548585484eSchristos event_add(periodic_level_check, ms100_common); 4558585484eSchristos 4568585484eSchristos if (cfg_group_drain && ratelim_group) { 4578585484eSchristos group_drain_event = event_new(base, -1, EV_PERSIST, group_drain_cb, NULL); 4588585484eSchristos event_add(group_drain_event, &cfg_tick); 4598585484eSchristos } 4608585484eSchristos 4618585484eSchristos event_base_dispatch(base); 4628585484eSchristos 4638585484eSchristos ratelim_group = NULL; /* So no more responders get added */ 4648585484eSchristos event_free(periodic_level_check); 4658585484eSchristos if (group_drain_event) 466*eabc0478Schristos event_free(group_drain_event); 4678585484eSchristos 4688585484eSchristos for (i = 0; i < cfg_n_connections; ++i) { 4698585484eSchristos bufferevent_free(bevs[i]); 4708585484eSchristos } 4718585484eSchristos evconnlistener_free(listener); 4728585484eSchristos 4738585484eSchristos /* Make sure no new echo_conns get added to the group. */ 4748585484eSchristos ratelim_group = NULL; 4758585484eSchristos 4768585484eSchristos /* This should get _everybody_ freed */ 4778585484eSchristos while (n_echo_conns_open) { 4788585484eSchristos printf("waiting for %d conns\n", n_echo_conns_open); 4798585484eSchristos tv.tv_sec = 0; 4808585484eSchristos tv.tv_usec = 300000; 4818585484eSchristos event_base_loopexit(base, &tv); 4828585484eSchristos event_base_dispatch(base); 4838585484eSchristos } 4848585484eSchristos 4858585484eSchristos if (group) 4868585484eSchristos bufferevent_rate_limit_group_free(group); 4878585484eSchristos 4888585484eSchristos if (total_n_bev_checks) { 4898585484eSchristos printf("Average read bucket level: %f\n", 4908585484eSchristos (double)total_rbucket_level/total_n_bev_checks); 4918585484eSchristos printf("Average write bucket level: %f\n", 4928585484eSchristos (double)total_wbucket_level/total_n_bev_checks); 4938585484eSchristos printf("Highest read bucket level: %f\n", 4948585484eSchristos (double)max_bucket_level); 4958585484eSchristos printf("Highest write bucket level: %f\n", 4968585484eSchristos (double)min_bucket_level); 4978585484eSchristos printf("Average max-to-read: %f\n", 4988585484eSchristos ((double)total_max_to_read)/total_n_bev_checks); 4998585484eSchristos printf("Average max-to-write: %f\n", 5008585484eSchristos ((double)total_max_to_write)/total_n_bev_checks); 5018585484eSchristos } 5028585484eSchristos if (total_n_group_bev_checks) { 5038585484eSchristos printf("Average group read bucket level: %f\n", 5048585484eSchristos ((double)total_group_rbucket_level)/total_n_group_bev_checks); 5058585484eSchristos printf("Average group write bucket level: %f\n", 5068585484eSchristos ((double)total_group_wbucket_level)/total_n_group_bev_checks); 5078585484eSchristos } 5088585484eSchristos 5098585484eSchristos total_received = 0; 5108585484eSchristos total_persec = 0.0; 5118585484eSchristos total_sq_persec = 0.0; 5128585484eSchristos for (i=0; i < cfg_n_connections; ++i) { 5138585484eSchristos double persec = states[i].received; 5148585484eSchristos persec /= cfg_duration; 5158585484eSchristos total_received += states[i].received; 5168585484eSchristos total_persec += persec; 5178585484eSchristos total_sq_persec += persec*persec; 5188585484eSchristos printf("%d: %f per second\n", i+1, persec); 5198585484eSchristos } 5208585484eSchristos printf(" total: %f per second\n", 5218585484eSchristos ((double)total_received)/cfg_duration); 5228585484eSchristos if (expected_total_persec > 0) { 5238585484eSchristos double diff = expected_total_persec - 5248585484eSchristos ((double)total_received/cfg_duration); 5258585484eSchristos printf(" [Off by %lf]\n", diff); 5268585484eSchristos if (cfg_grouplimit_tolerance > 0 && 5278585484eSchristos fabs(diff) > cfg_grouplimit_tolerance) { 5288585484eSchristos fprintf(stderr, "Group bandwidth out of bounds\n"); 5298585484eSchristos ok = 0; 5308585484eSchristos } 5318585484eSchristos } 5328585484eSchristos 5338585484eSchristos printf(" average: %f per second\n", 5348585484eSchristos (((double)total_received)/cfg_duration)/cfg_n_connections); 5358585484eSchristos if (expected_avg_persec > 0) { 5368585484eSchristos double diff = expected_avg_persec - (((double)total_received)/cfg_duration)/cfg_n_connections; 5378585484eSchristos printf(" [Off by %lf]\n", diff); 5388585484eSchristos if (cfg_connlimit_tolerance > 0 && 5398585484eSchristos fabs(diff) > cfg_connlimit_tolerance) { 5408585484eSchristos fprintf(stderr, "Connection bandwidth out of bounds\n"); 5418585484eSchristos ok = 0; 5428585484eSchristos } 5438585484eSchristos } 5448585484eSchristos 5458585484eSchristos variance = total_sq_persec/cfg_n_connections - total_persec*total_persec/(cfg_n_connections*cfg_n_connections); 5468585484eSchristos 5478585484eSchristos printf(" stddev: %f per second\n", sqrt(variance)); 5488585484eSchristos if (cfg_stddev_tolerance > 0 && 5498585484eSchristos sqrt(variance) > cfg_stddev_tolerance) { 5508585484eSchristos fprintf(stderr, "Connection variance out of bounds\n"); 5518585484eSchristos ok = 0; 5528585484eSchristos } 5538585484eSchristos 5548585484eSchristos event_base_free(base); 5558585484eSchristos free(bevs); 5568585484eSchristos free(states); 5578585484eSchristos 5588585484eSchristos return ok ? 0 : 1; 5598585484eSchristos } 5608585484eSchristos 5618585484eSchristos static struct option { 5628585484eSchristos const char *name; int *ptr; int min; int isbool; 5638585484eSchristos } options[] = { 5648585484eSchristos { "-v", &cfg_verbose, 0, 1 }, 5658585484eSchristos { "-h", &cfg_help, 0, 1 }, 5668585484eSchristos { "-n", &cfg_n_connections, 1, 0 }, 5678585484eSchristos { "-d", &cfg_duration, 1, 0 }, 5688585484eSchristos { "-c", &cfg_connlimit, 0, 0 }, 5698585484eSchristos { "-g", &cfg_grouplimit, 0, 0 }, 5708585484eSchristos { "-G", &cfg_group_drain, -100000, 0 }, 5718585484eSchristos { "-t", &cfg_tick_msec, 10, 0 }, 5728585484eSchristos { "--min-share", &cfg_min_share, 0, 0 }, 5738585484eSchristos { "--check-connlimit", &cfg_connlimit_tolerance, 0, 0 }, 5748585484eSchristos { "--check-grouplimit", &cfg_grouplimit_tolerance, 0, 0 }, 5758585484eSchristos { "--check-stddev", &cfg_stddev_tolerance, 0, 0 }, 5768585484eSchristos #ifdef _WIN32 5778585484eSchristos { "--iocp", &cfg_enable_iocp, 0, 1 }, 5788585484eSchristos #endif 5798585484eSchristos { NULL, NULL, -1, 0 }, 5808585484eSchristos }; 5818585484eSchristos 5828585484eSchristos static int 5838585484eSchristos handle_option(int argc, char **argv, int *i, const struct option *opt) 5848585484eSchristos { 5858585484eSchristos long val; 5868585484eSchristos char *endptr = NULL; 5878585484eSchristos if (opt->isbool) { 5888585484eSchristos *opt->ptr = 1; 5898585484eSchristos return 0; 5908585484eSchristos } 5918585484eSchristos if (*i + 1 == argc) { 5928585484eSchristos fprintf(stderr, "Too few arguments to '%s'\n",argv[*i]); 5938585484eSchristos return -1; 5948585484eSchristos } 5958585484eSchristos val = strtol(argv[*i+1], &endptr, 10); 5968585484eSchristos if (*argv[*i+1] == '\0' || !endptr || *endptr != '\0') { 5978585484eSchristos fprintf(stderr, "Couldn't parse numeric value '%s'\n", 5988585484eSchristos argv[*i+1]); 5998585484eSchristos return -1; 6008585484eSchristos } 6018585484eSchristos if (val < opt->min || val > 0x7fffffff) { 6028585484eSchristos fprintf(stderr, "Value '%s' is out-of-range'\n", 6038585484eSchristos argv[*i+1]); 6048585484eSchristos return -1; 6058585484eSchristos } 6068585484eSchristos *opt->ptr = (int)val; 6078585484eSchristos ++*i; 6088585484eSchristos return 0; 6098585484eSchristos } 6108585484eSchristos 6118585484eSchristos static void 6128585484eSchristos usage(void) 6138585484eSchristos { 6148585484eSchristos fprintf(stderr, 6158585484eSchristos "test-ratelim [-v] [-n INT] [-d INT] [-c INT] [-g INT] [-t INT]\n\n" 6168585484eSchristos "Pushes bytes through a number of possibly rate-limited connections, and\n" 6178585484eSchristos "displays average throughput.\n\n" 6188585484eSchristos " -n INT: Number of connections to open (default: 30)\n" 6198585484eSchristos " -d INT: Duration of the test in seconds (default: 5 sec)\n"); 6208585484eSchristos fprintf(stderr, 6218585484eSchristos " -c INT: Connection-rate limit applied to each connection in bytes per second\n" 6228585484eSchristos " (default: None.)\n" 6238585484eSchristos " -g INT: Group-rate limit applied to sum of all usage in bytes per second\n" 6248585484eSchristos " (default: None.)\n" 6258585484eSchristos " -G INT: drain INT bytes from the group limit every tick. (default: 0)\n" 6268585484eSchristos " -t INT: Granularity of timing, in milliseconds (default: 1000 msec)\n"); 6278585484eSchristos } 6288585484eSchristos 6298585484eSchristos int 6308585484eSchristos main(int argc, char **argv) 6318585484eSchristos { 6328585484eSchristos int i,j; 6338585484eSchristos double ratio; 6348585484eSchristos 6358585484eSchristos #ifdef _WIN32 6368585484eSchristos WORD wVersionRequested = MAKEWORD(2,2); 6378585484eSchristos WSADATA wsaData; 6388585484eSchristos 6398585484eSchristos (void) WSAStartup(wVersionRequested, &wsaData); 6408585484eSchristos #endif 6418585484eSchristos 6427476e6e4Schristos evutil_weakrand_seed_(&weakrand_state, 0); 6437476e6e4Schristos 6448585484eSchristos #ifndef _WIN32 6458585484eSchristos if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) 6468585484eSchristos return 1; 6478585484eSchristos #endif 6488585484eSchristos for (i = 1; i < argc; ++i) { 6498585484eSchristos for (j = 0; options[j].name; ++j) { 6508585484eSchristos if (!strcmp(argv[i],options[j].name)) { 6518585484eSchristos if (handle_option(argc,argv,&i,&options[j])<0) 6528585484eSchristos return 1; 6538585484eSchristos goto again; 6548585484eSchristos } 6558585484eSchristos } 6568585484eSchristos fprintf(stderr, "Unknown option '%s'\n", argv[i]); 6578585484eSchristos usage(); 6588585484eSchristos return 1; 6598585484eSchristos again: 6608585484eSchristos ; 6618585484eSchristos } 6628585484eSchristos if (cfg_help) { 6638585484eSchristos usage(); 6648585484eSchristos return 0; 6658585484eSchristos } 6668585484eSchristos 6678585484eSchristos cfg_tick.tv_sec = cfg_tick_msec / 1000; 6688585484eSchristos cfg_tick.tv_usec = (cfg_tick_msec % 1000)*1000; 6698585484eSchristos 6708585484eSchristos seconds_per_tick = ratio = cfg_tick_msec / 1000.0; 6718585484eSchristos 6728585484eSchristos cfg_connlimit *= ratio; 6738585484eSchristos cfg_grouplimit *= ratio; 6748585484eSchristos 6758585484eSchristos { 6768585484eSchristos struct timeval tv; 6778585484eSchristos evutil_gettimeofday(&tv, NULL); 6788585484eSchristos #ifdef _WIN32 6798585484eSchristos srand(tv.tv_usec); 6808585484eSchristos #else 6818585484eSchristos srandom(tv.tv_usec); 6828585484eSchristos #endif 6838585484eSchristos } 6848585484eSchristos 6858585484eSchristos #ifndef EVENT__DISABLE_THREAD_SUPPORT 6868585484eSchristos evthread_enable_lock_debugging(); 6878585484eSchristos #endif 6888585484eSchristos 6898585484eSchristos return test_ratelimiting(); 6908585484eSchristos } 691