r9702: r9680@blu: tridge | 2005-08-27 18:45:08 +1000
[metze/samba/wip.git] / source4 / torture / nbt / winsbench.c
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 2 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, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "lib/events/events.h"
25 #include "libcli/nbt/libnbt.h"
26 #include "librpc/gen_ndr/ndr_nbt.h"
27 #include "lib/socket/socket.h"
28
29 struct wins_state {
30         int num_names;
31         BOOL *registered;
32         int pass_count;
33         int fail_count;
34         const char *wins_server;
35         const char *my_ip;
36         uint32_t ttl;
37 };
38
39 struct idx_state {
40         int idx;
41         struct wins_state *state;
42 };
43
44 struct nbt_name generate_name(TALLOC_CTX *mem_ctx, int idx)
45 {
46         struct nbt_name name;
47         name.name       = talloc_asprintf(mem_ctx, "WINSBench%6u", idx);
48         name.type       = 0x4;
49         name.scope      = NULL;
50         return name;
51 }
52
53 static void register_handler(struct nbt_name_request *req)
54 {
55         struct idx_state *istate = talloc_get_type(req->async.private, struct idx_state);
56         struct wins_state *state = istate->state;
57         struct nbt_name_register io;
58         NTSTATUS status;
59
60         status = nbt_name_register_recv(req, istate, &io);
61         if (!NT_STATUS_IS_OK(status) || io.out.rcode != NBT_RCODE_OK) {
62                 state->fail_count++;
63         } else {
64                 state->pass_count++;
65                 state->registered[istate->idx] = True;
66         }
67         talloc_free(istate);    
68 }
69
70 /*
71   generate a registration
72 */
73 static void generate_register(struct nbt_name_socket *nbtsock, struct wins_state *state, int idx)
74 {
75         struct nbt_name_register io;
76         TALLOC_CTX *tmp_ctx = talloc_new(state);
77         struct nbt_name_request *req;
78         struct idx_state *istate;
79
80         istate = talloc(nbtsock, struct idx_state);
81         istate->idx = idx;
82         istate->state = state;
83
84         io.in.name            = generate_name(tmp_ctx, idx);
85         io.in.dest_addr       = state->wins_server;
86         io.in.address         = state->my_ip;
87         io.in.nb_flags        = NBT_NODE_H;
88         io.in.register_demand = False;
89         io.in.broadcast       = False;
90         io.in.multi_homed     = False;
91         io.in.ttl             = state->ttl;
92         io.in.timeout         = 2;
93         io.in.retries         = 1;
94
95         req = nbt_name_register_send(nbtsock, &io);
96
97         req->async.fn = register_handler;
98         req->async.private = istate;
99
100         talloc_free(tmp_ctx);
101 }
102
103
104 static void release_handler(struct nbt_name_request *req)
105 {
106         struct idx_state *istate = talloc_get_type(req->async.private, struct idx_state);
107         struct wins_state *state = istate->state;
108         struct nbt_name_release io;
109         NTSTATUS status;
110
111         status = nbt_name_release_recv(req, istate, &io);
112         if (state->registered[istate->idx] && 
113             (!NT_STATUS_IS_OK(status) || io.out.rcode != NBT_RCODE_OK)) {
114                 state->fail_count++;
115         } else {
116                 state->pass_count++;
117                 state->registered[istate->idx] = False;
118         }
119         talloc_free(istate);    
120 }
121
122 /*
123   generate a name release
124 */
125 static void generate_release(struct nbt_name_socket *nbtsock, struct wins_state *state, int idx)
126 {
127         struct nbt_name_release io;
128         TALLOC_CTX *tmp_ctx = talloc_new(state);
129         struct nbt_name_request *req;
130         struct idx_state *istate;
131
132         istate = talloc(nbtsock, struct idx_state);
133         istate->idx = idx;
134         istate->state = state;
135
136         io.in.name            = generate_name(tmp_ctx, idx);
137         io.in.dest_addr       = state->wins_server;
138         io.in.address         = state->my_ip;
139         io.in.nb_flags        = NBT_NODE_H;
140         io.in.broadcast       = False;
141         io.in.timeout         = 2;
142         io.in.retries         = 1;
143
144         req = nbt_name_release_send(nbtsock, &io);
145
146         req->async.fn = release_handler;
147         req->async.private = istate;
148
149         talloc_free(tmp_ctx);
150 }
151
152
153 static void query_handler(struct nbt_name_request *req)
154 {
155         struct idx_state *istate = talloc_get_type(req->async.private, struct idx_state);
156         struct wins_state *state = istate->state;
157         struct nbt_name_query io;
158         NTSTATUS status;
159
160         status = nbt_name_query_recv(req, istate, &io);
161         if (!NT_STATUS_IS_OK(status) && state->registered[istate->idx]) {
162                 state->fail_count++;
163         } else {
164                 state->pass_count++;
165         }
166         talloc_free(istate);    
167 }
168
169 /*
170   generate a name query
171 */
172 static void generate_query(struct nbt_name_socket *nbtsock, struct wins_state *state, int idx)
173 {
174         struct nbt_name_query io;
175         TALLOC_CTX *tmp_ctx = talloc_new(state);
176         struct nbt_name_request *req;
177         struct idx_state *istate;
178
179         istate = talloc(nbtsock, struct idx_state);
180         istate->idx = idx;
181         istate->state = state;
182
183         io.in.name            = generate_name(tmp_ctx, idx);
184         io.in.dest_addr       = state->wins_server;
185         io.in.broadcast       = False;
186         io.in.wins_lookup     = True;
187         io.in.timeout         = 2;
188         io.in.retries         = 1;
189
190         req = nbt_name_query_send(nbtsock, &io);
191
192         req->async.fn = query_handler;
193         req->async.private = istate;
194
195         talloc_free(tmp_ctx);
196 }
197
198 /*
199   generate one WINS request
200 */
201 static void generate_request(struct nbt_name_socket *nbtsock, struct wins_state *state, int idx)
202 {
203         if (random() % 5 == 0) {
204                 generate_register(nbtsock, state, idx);
205                 return;
206         }
207
208         if (random() % 20 == 0) {
209                 generate_release(nbtsock, state, idx);
210                 return;
211         }
212
213         generate_query(nbtsock, state, idx);
214 }
215
216 /*
217   benchmark simple name queries
218 */
219 static BOOL bench_wins(TALLOC_CTX *mem_ctx, struct nbt_name *name, const char *address)
220 {
221         struct nbt_name_socket *nbtsock = nbt_name_socket_init(mem_ctx, NULL);
222         int num_sent=0;
223         struct timeval tv = timeval_current();
224         BOOL ret = True;
225         int timelimit = lp_parm_int(-1, "torture", "timelimit", 10);
226         struct wins_state *state;
227         extern int torture_entries;
228
229         state = talloc_zero(nbtsock, struct wins_state);
230
231         state->num_names = torture_entries;
232         state->registered = talloc_zero_array(state, BOOL, state->num_names);
233         state->wins_server = address;
234         state->my_ip = talloc_strdup(mem_ctx, iface_best_ip(address));
235         state->ttl = timelimit;
236
237         socket_listen(nbtsock->sock, state->my_ip, 0, 0, 0);
238
239         printf("Running for %d seconds\n", timelimit);
240         while (timeval_elapsed(&tv) < timelimit) {
241                 while (num_sent - (state->pass_count+state->fail_count) < 10) {
242                         generate_request(nbtsock, state, num_sent % state->num_names);
243                         num_sent++;
244                         if (num_sent % 50 == 0) {
245                                 printf("%.1f queries per second (%d failures)  \r", 
246                                        state->pass_count / timeval_elapsed(&tv),
247                                        state->fail_count);
248                         }
249                 }
250
251                 event_loop_once(nbtsock->event_ctx);
252         }
253
254         while (num_sent != (state->pass_count + state->fail_count)) {
255                 event_loop_once(nbtsock->event_ctx);
256         }
257
258         printf("%.1f queries per second (%d failures)  \n", 
259                state->pass_count / timeval_elapsed(&tv),
260                state->fail_count);
261
262         talloc_free(nbtsock);
263         return ret;
264 }
265
266
267 /*
268   benchmark how fast a WINS server can respond to a mixture of
269   registration/refresh/release and name query requests
270 */
271 BOOL torture_bench_wins(void)
272 {
273         const char *address;
274         struct nbt_name name;
275         TALLOC_CTX *mem_ctx = talloc_new(NULL);
276         NTSTATUS status;
277         BOOL ret = True;
278         
279         make_nbt_name_server(&name, lp_parm_string(-1, "torture", "host"));
280
281         /* do an initial name resolution to find its IP */
282         status = resolve_name(&name, mem_ctx, &address, event_context_find(mem_ctx));
283         if (!NT_STATUS_IS_OK(status)) {
284                 printf("Failed to resolve %s - %s\n",
285                        name.name, nt_errstr(status));
286                 talloc_free(mem_ctx);
287                 return False;
288         }
289
290         ret &= bench_wins(mem_ctx, &name, address);
291
292         talloc_free(mem_ctx);
293
294         return ret;
295 }