1/* 2 Unix SMB/CIFS implementation. 3 4 WINS benchmark test 5 6 Copyright (C) Andrew Tridgell 2005 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 3 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program. If not, see <http://www.gnu.org/licenses/>. 20*/ 21 22#include "includes.h" 23#include "lib/events/events.h" 24#include "lib/socket/socket.h" 25#include "libcli/resolve/resolve.h" 26#include "system/network.h" 27#include "lib/socket/netif.h" 28#include "torture/torture.h" 29#include "torture/nbt/proto.h" 30#include "param/param.h" 31 32struct wins_state { 33 int num_names; 34 bool *registered; 35 int pass_count; 36 int fail_count; 37 const char *wins_server; 38 uint16_t wins_port; 39 const char *my_ip; 40 uint32_t ttl; 41}; 42 43struct idx_state { 44 int idx; 45 struct wins_state *state; 46}; 47 48static struct nbt_name generate_name(TALLOC_CTX *tctx, int idx) 49{ 50 struct nbt_name name; 51 name.name = talloc_asprintf(tctx, "WINSBench%6u", idx); 52 name.type = 0x4; 53 name.scope = NULL; 54 return name; 55} 56 57static void register_handler(struct nbt_name_request *req) 58{ 59 struct idx_state *istate = talloc_get_type(req->async.private_data, struct idx_state); 60 struct wins_state *state = istate->state; 61 struct nbt_name_register io; 62 NTSTATUS status; 63 64 status = nbt_name_register_recv(req, istate, &io); 65 if (!NT_STATUS_IS_OK(status) || io.out.rcode != NBT_RCODE_OK) { 66 state->fail_count++; 67 } else { 68 state->pass_count++; 69 state->registered[istate->idx] = true; 70 } 71 talloc_free(istate); 72} 73 74/* 75 generate a registration 76*/ 77static void generate_register(struct nbt_name_socket *nbtsock, struct wins_state *state, int idx) 78{ 79 struct nbt_name_register io; 80 TALLOC_CTX *tmp_ctx = talloc_new(state); 81 struct nbt_name_request *req; 82 struct idx_state *istate; 83 84 istate = talloc(nbtsock, struct idx_state); 85 istate->idx = idx; 86 istate->state = state; 87 88 io.in.name = generate_name(tmp_ctx, idx); 89 io.in.dest_addr = state->wins_server; 90 io.in.dest_port = state->wins_port; 91 io.in.address = state->my_ip; 92 io.in.nb_flags = NBT_NODE_H; 93 io.in.register_demand = false; 94 io.in.broadcast = false; 95 io.in.multi_homed = false; 96 io.in.ttl = state->ttl; 97 io.in.timeout = 2; 98 io.in.retries = 1; 99 100 req = nbt_name_register_send(nbtsock, &io); 101 102 req->async.fn = register_handler; 103 req->async.private_data = istate; 104 105 talloc_free(tmp_ctx); 106} 107 108 109static void release_handler(struct nbt_name_request *req) 110{ 111 struct idx_state *istate = talloc_get_type(req->async.private_data, struct idx_state); 112 struct wins_state *state = istate->state; 113 struct nbt_name_release io; 114 NTSTATUS status; 115 116 status = nbt_name_release_recv(req, istate, &io); 117 if (state->registered[istate->idx] && 118 (!NT_STATUS_IS_OK(status) || io.out.rcode != NBT_RCODE_OK)) { 119 state->fail_count++; 120 } else { 121 state->pass_count++; 122 state->registered[istate->idx] = false; 123 } 124 talloc_free(istate); 125} 126 127/* 128 generate a name release 129*/ 130static void generate_release(struct nbt_name_socket *nbtsock, struct wins_state *state, int idx) 131{ 132 struct nbt_name_release io; 133 TALLOC_CTX *tmp_ctx = talloc_new(state); 134 struct nbt_name_request *req; 135 struct idx_state *istate; 136 137 istate = talloc(nbtsock, struct idx_state); 138 istate->idx = idx; 139 istate->state = state; 140 141 io.in.name = generate_name(tmp_ctx, idx); 142 io.in.dest_port = state->wins_port; 143 io.in.dest_addr = state->wins_server; 144 io.in.address = state->my_ip; 145 io.in.nb_flags = NBT_NODE_H; 146 io.in.broadcast = false; 147 io.in.timeout = 2; 148 io.in.retries = 1; 149 150 req = nbt_name_release_send(nbtsock, &io); 151 152 req->async.fn = release_handler; 153 req->async.private_data = istate; 154 155 talloc_free(tmp_ctx); 156} 157 158 159static void query_handler(struct nbt_name_request *req) 160{ 161 struct idx_state *istate = talloc_get_type(req->async.private_data, struct idx_state); 162 struct wins_state *state = istate->state; 163 struct nbt_name_query io; 164 NTSTATUS status; 165 166 status = nbt_name_query_recv(req, istate, &io); 167 if (!NT_STATUS_IS_OK(status) && state->registered[istate->idx]) { 168 state->fail_count++; 169 } else { 170 state->pass_count++; 171 } 172 talloc_free(istate); 173} 174 175/* 176 generate a name query 177*/ 178static void generate_query(struct nbt_name_socket *nbtsock, struct wins_state *state, int idx) 179{ 180 struct nbt_name_query io; 181 TALLOC_CTX *tmp_ctx = talloc_new(state); 182 struct nbt_name_request *req; 183 struct idx_state *istate; 184 185 istate = talloc(nbtsock, struct idx_state); 186 istate->idx = idx; 187 istate->state = state; 188 189 io.in.name = generate_name(tmp_ctx, idx); 190 io.in.dest_addr = state->wins_server; 191 io.in.dest_port = state->wins_port; 192 io.in.broadcast = false; 193 io.in.wins_lookup = true; 194 io.in.timeout = 2; 195 io.in.retries = 1; 196 197 req = nbt_name_query_send(nbtsock, &io); 198 199 req->async.fn = query_handler; 200 req->async.private_data = istate; 201 202 talloc_free(tmp_ctx); 203} 204 205/* 206 generate one WINS request 207*/ 208static void generate_request(struct nbt_name_socket *nbtsock, struct wins_state *state, int idx) 209{ 210 if (random() % 5 == 0) { 211 generate_register(nbtsock, state, idx); 212 return; 213 } 214 215 if (random() % 20 == 0) { 216 generate_release(nbtsock, state, idx); 217 return; 218 } 219 220 generate_query(nbtsock, state, idx); 221} 222 223/* 224 benchmark simple name queries 225*/ 226static bool bench_wins(struct torture_context *tctx) 227{ 228 struct nbt_name_socket *nbtsock = nbt_name_socket_init(tctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx)); 229 int num_sent=0; 230 struct timeval tv = timeval_current(); 231 bool ret = true; 232 int timelimit = torture_setting_int(tctx, "timelimit", 5); 233 struct wins_state *state; 234 extern int torture_entries; 235 struct socket_address *my_ip; 236 struct nbt_name name; 237 const char *address; 238 struct interface *ifaces; 239 240 if (!torture_nbt_get_name(tctx, &name, &address)) 241 return false; 242 243 state = talloc_zero(nbtsock, struct wins_state); 244 245 state->num_names = torture_entries; 246 state->registered = talloc_zero_array(state, bool, state->num_names); 247 state->wins_server = address; 248 state->wins_port = lp_nbt_port(tctx->lp_ctx); 249 load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces); 250 state->my_ip = talloc_strdup(tctx, iface_best_ip(ifaces, address)); 251 state->ttl = timelimit; 252 253 my_ip = socket_address_from_strings(nbtsock, nbtsock->sock->backend_name, 254 state->my_ip, 0); 255 256 socket_listen(nbtsock->sock, my_ip, 0, 0); 257 258 torture_comment(tctx, "Running for %d seconds\n", timelimit); 259 while (timeval_elapsed(&tv) < timelimit) { 260 while (num_sent - (state->pass_count+state->fail_count) < 10) { 261 generate_request(nbtsock, state, num_sent % state->num_names); 262 num_sent++; 263 if (num_sent % 50 == 0) { 264 if (torture_setting_bool(tctx, "progress", true)) { 265 torture_comment(tctx, "%.1f queries per second (%d failures) \r", 266 state->pass_count / timeval_elapsed(&tv), 267 state->fail_count); 268 fflush(stdout); 269 } 270 } 271 } 272 273 event_loop_once(nbtsock->event_ctx); 274 } 275 276 while (num_sent != (state->pass_count + state->fail_count)) { 277 event_loop_once(nbtsock->event_ctx); 278 } 279 280 torture_comment(tctx, "%.1f queries per second (%d failures) \n", 281 state->pass_count / timeval_elapsed(&tv), 282 state->fail_count); 283 284 talloc_free(nbtsock); 285 return ret; 286} 287 288 289/* 290 benchmark how fast a WINS server can respond to a mixture of 291 registration/refresh/release and name query requests 292*/ 293struct torture_suite *torture_bench_wins(TALLOC_CTX *mem_ctx) 294{ 295 struct torture_suite *suite = torture_suite_create(mem_ctx, 296 "BENCH-WINS"); 297 298 torture_suite_add_simple_test(suite, "wins", bench_wins); 299 300 return suite; 301} 302