Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / rxkad / test / stress_c.c
1 /*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
4 *
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
8 */
9
10 /* RX Authentication Stress test: client side code. */
11
12 #include <afsconfig.h>
13 #include <afs/param.h>
14
15 #include <roken.h>
16
17 #include <afs/stds.h>
18
19 #include <afs/com_err.h>
20 #include <afs/afsutil.h>
21 #include <rx/rxkad.h>
22 #include <afs/auth.h>
23 #include <des_prototypes.h>
24 #include "stress.h"
25 #include "stress_internal.h"
26 #ifdef AFS_PTHREAD_ENV
27 #include <pthread.h>
28 #define FT_ApproxTime() (int)time(0)
29 #endif
30
31 extern int maxSkew;
32
33 static char *whoami;
34
35 static long
36 GetServer(const char *aname)
37 {
38 struct hostent *th;
39 long addr;
40
41 th = gethostbyname(aname);
42 if (!th) {
43 fprintf(stderr, "host %s not found\n", aname);
44 return errno;
45 }
46 memcpy(&addr, th->h_addr, sizeof(addr));
47 return addr;
48 }
49
50 static long
51 GetToken(long *versionP, struct ktc_encryptionKey *session,
52 int *ticketLenP, char *ticket, char *cell)
53 {
54 struct ktc_principal sname;
55 struct ktc_token ttoken;
56 long code;
57
58 strcpy(sname.cell, cell);
59 sname.instance[0] = 0;
60 strcpy(sname.name, "afs");
61 code = ktc_GetToken(&sname, &ttoken, sizeof(ttoken), NULL);
62 if (code)
63 return code;
64
65 *versionP = ttoken.kvno;
66 *ticketLenP = ttoken.ticketLen;
67 memcpy(ticket, ttoken.ticket, ttoken.ticketLen);
68 memcpy(session, &ttoken.sessionKey, sizeof(struct ktc_encryptionKey));
69 return 0;
70 }
71
72 static long
73 GetTicket(long *versionP, struct ktc_encryptionKey *session,
74 int *ticketLenP, char *ticket, char *cell)
75 {
76 long code;
77
78 /* create random session key, using key for seed to good random */
79 des_init_random_number_generator(ktc_to_cblock(&serviceKey));
80 code = des_random_key(ktc_to_cblock(session));
81 if (code)
82 return code;
83
84 /* now create the actual ticket */
85 *ticketLenP = 0;
86 code =
87 tkt_MakeTicket(ticket, ticketLenP, &serviceKey, RXKST_CLIENT_NAME,
88 RXKST_CLIENT_INST, cell,
89 /*start,end */ 0, 0xffffffff, session, /*host */ 0,
90 RXKST_SERVER_NAME, RXKST_SERVER_NAME);
91 /* parms were buffer, ticketlen, key to seal ticket with, principal name,
92 * instance and cell, start time, end time, session key to seal in ticket,
93 * inet host, server name and server instance */
94 if (code)
95 return code;
96 *versionP = serviceKeyVersion;
97 return 0;
98 }
99 struct client {
100 struct rx_connection *conn;
101 u_long sendLen; /* parameters for call to Copious */
102 u_long recvLen;
103 u_long *fastCalls; /* number of calls to perform */
104 u_long *slowCalls;
105 u_long *copiousCalls;
106 };
107
108 static long
109 Copious(struct client *c, char *buf, u_long buflen)
110 {
111 long code;
112 struct rx_call *call;
113 long i;
114 long inlen = c->sendLen;
115 long outlen = c->recvLen;
116 long d = 23;
117 long mysum;
118 size_t outsum;
119
120 mysum = 0;
121 for (i = 0; i < inlen; i++)
122 mysum += (d++ & 0xff);
123
124 call = rx_NewCall(c->conn);
125 code = StartRXKST_Copious(call, inlen, mysum, outlen);
126 if (code == 0) {
127 long tlen;
128 long xfer = 0;
129 long n;
130 d = 23;
131 while (xfer < inlen) {
132 tlen = inlen - xfer;
133 if (tlen > buflen)
134 tlen = buflen;
135 for (i = 0; i < tlen; i++)
136 buf[i] = (d++ & 0xff);
137 n = rx_Write(call, buf, tlen);
138 if (n != tlen) {
139 if (n < 0)
140 code = n;
141 else
142 code = RXKST_WRITESHORT;
143 break;
144 }
145 xfer += tlen;
146 }
147 if (code == 0) {
148 xfer = 0;
149 mysum = 0;
150 while (xfer < outlen) {
151 tlen = outlen - xfer;
152 if (tlen > buflen)
153 tlen = buflen;
154 n = rx_Read(call, buf, tlen);
155 if (n != tlen) {
156 if (n < 0)
157 code = n;
158 else
159 code = RXKST_READSHORT;
160 break;
161 }
162 for (i = 0; i < tlen; i++)
163 mysum += buf[i];
164 xfer += tlen;
165 }
166 }
167 }
168 if (code == 0)
169 code = EndRXKST_Copious(call, &outsum);
170 code = rx_EndCall(call, code);
171 if (code)
172 return code;
173 if (outsum != mysum) {
174 return RXKST_BADOUTPUTSUM;
175 }
176 return 0;
177 }
178
179 static long
180 DoClient(int index, opaque rock)
181 {
182 struct client *c = (struct client *)rock;
183 long code = 0;
184 int i;
185 u_long n, inc_n;
186
187 n = 95678;
188 for (i = 0; i < c->fastCalls[index]; i++) {
189 code = RXKST_Fast(c->conn, n, &inc_n);
190 if (code)
191 return (code);
192 if (n + 1 != inc_n)
193 return RXKST_INCFAILED;
194 n++;
195 }
196
197 for (i = 0; i < c->slowCalls[index]; i++) {
198 u_long ntime;
199 u_long now;
200 code = RXKST_Slow(c->conn, 1, &ntime);
201 if (code)
202 return (code);
203 now = FT_ApproxTime();
204 if ((ntime < now - maxSkew) || (ntime > now + maxSkew))
205 return RXKST_TIMESKEW;
206 }
207
208 if (c->copiousCalls[index] > 0) {
209 u_long buflen = 10000;
210 char *buf = osi_Alloc(buflen);
211 for (i = 0; i < c->copiousCalls[index]; i++) {
212 code = Copious(c, buf, buflen);
213 if (code)
214 break;
215 }
216 osi_Free(buf, buflen);
217 if (code)
218 return code;
219 }
220 return 0;
221 }
222
223 struct worker {
224 struct worker *next;
225 long exitCode; /* is PROCESSRUNNING until exit */
226 int index;
227 opaque rock;
228 long (*proc) (int, opaque);
229 };
230
231 #ifdef AFS_PTHREAD_ENV
232 static pthread_once_t workerOnce = PTHREAD_ONCE_INIT;
233 static pthread_mutex_t workerLock;
234 static pthread_cond_t workerCV;
235 void
236 WorkerInit(void)
237 {
238 pthread_mutex_init(&workerLock, NULL);
239 pthread_cond_init(&workerCV, NULL);
240 }
241 #endif
242 static struct worker *workers;
243
244 static void *
245 DoWorker(void * rock)
246 {
247 struct worker *w = rock;
248 long code;
249 code = (*w->proc) (w->index, w->rock);
250 #ifdef AFS_PTHREAD_ENV
251 pthread_mutex_lock(&workerLock);
252 #endif
253 w->exitCode = code;
254 #ifdef AFS_PTHREAD_ENV
255 pthread_mutex_unlock(&workerLock);
256 #endif
257 #ifdef AFS_PTHREAD_ENV
258 pthread_cond_signal(&workerCV);
259 #else
260 LWP_NoYieldSignal(&workers);
261 #endif
262 return (void *)(intptr_t)code;
263 }
264
265 #define MAX_CTHREADS 25
266
267 static long
268 CallSimultaneously(u_int threads, opaque rock, long (*proc)(int, opaque))
269 {
270 long code;
271 int i;
272 #ifdef AFS_PTHREAD_ENV
273 pthread_once(&workerOnce, WorkerInit);
274 #endif
275
276 workers = 0;
277 for (i = 0; i < threads; i++) {
278 struct worker *w;
279 #ifdef AFS_PTHREAD_ENV
280 pthread_t pid;
281 #else
282 PROCESS pid;
283 #endif
284 assert(i < MAX_CTHREADS);
285 w = osi_Alloc(sizeof(struct worker));
286 memset(w, 0, sizeof(*w));
287 w->next = workers;
288 workers = w;
289 w->index = i;
290 w->exitCode = RXKST_PROCESSRUNNING;
291 w->rock = rock;
292 w->proc = proc;
293 #ifdef AFS_PTHREAD_ENV
294 {
295 pthread_attr_t tattr;
296
297 code = pthread_attr_init(&tattr);
298 if (code) {
299 afs_com_err(whoami, code,
300 "can't pthread_attr_init worker process");
301 return code;
302 }
303
304 code =
305 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
306 if (code) {
307 afs_com_err(whoami, code,
308 "can't pthread_attr_setdetachstate worker process");
309 return code;
310 }
311
312 code = pthread_create(&pid, &tattr, DoWorker, (void *)w);
313 }
314 #else
315 code =
316 LWP_CreateProcess(DoWorker, 16000, LWP_NORMAL_PRIORITY,
317 (opaque) w, "Worker Process", &pid);
318 #endif
319 if (code) {
320 afs_com_err(whoami, code, "can't create worker process");
321 return code;
322 }
323 }
324 code = 0; /* last non-zero code encountered */
325 #ifdef AFS_PTHREAD_ENV
326 pthread_mutex_lock(&workerLock);
327 #endif
328 while (workers) {
329 struct worker *w, *prevW, *nextW;
330 prevW = 0;
331 for (w = workers; w; w = nextW) {
332 nextW = w->next;
333 if (w->exitCode != RXKST_PROCESSRUNNING) {
334 if (w->exitCode) {
335 if (code == 0)
336 code = w->exitCode;
337 }
338 if (prevW)
339 prevW->next = w->next;
340 else
341 workers = w->next;
342 osi_Free(w, sizeof(*w));
343 continue; /* don't bump prevW */
344 }
345 prevW = w;
346 }
347 #ifdef AFS_PTHREAD_ENV
348 if (workers)
349 pthread_cond_wait(&workerCV, &workerLock);
350 #else
351 if (workers)
352 LWP_WaitProcess(&workers);
353 #endif
354 }
355 #ifdef AFS_PTHREAD_ENV
356 pthread_mutex_unlock(&workerLock);
357 #endif
358 return code;
359 }
360
361 static void
362 DivideUpCalls(u_long calls, u_int threads, u_long threadCalls[])
363 {
364 int i;
365 for (i = 0; i < threads; i++) {
366 threadCalls[i] = calls / (threads - i);
367 calls -= threadCalls[i];
368 }
369 }
370
371 static double
372 ftime(void)
373 {
374 struct timeval tv;
375 gettimeofday(&tv, NULL);
376 return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
377 }
378
379 static long
380 RunLoadTest(struct clientParms *parms, struct rx_connection *conn)
381 {
382 long code;
383 struct client c;
384 u_long fastCalls[MAX_CTHREADS];
385 u_long slowCalls[MAX_CTHREADS];
386 u_long copiousCalls[MAX_CTHREADS];
387 double start, interval;
388
389 DivideUpCalls(parms->fastCalls, parms->threads, fastCalls);
390 DivideUpCalls(parms->slowCalls, parms->threads, slowCalls);
391 DivideUpCalls(parms->copiousCalls, parms->threads, copiousCalls);
392
393 memset(&c, 0, sizeof(c));
394 c.conn = conn;
395 c.sendLen = parms->sendLen;
396 c.recvLen = parms->recvLen;
397 c.fastCalls = fastCalls;
398 c.slowCalls = slowCalls;
399 c.copiousCalls = copiousCalls;
400
401 start = ftime();
402 code = CallSimultaneously(parms->threads, &c, DoClient);
403 if (code) {
404 afs_com_err(whoami, code, "in DoClient");
405 return code;
406 }
407 interval = ftime() - start;
408
409 if (parms->printTiming) {
410 u_long totalCalls =
411 parms->fastCalls + parms->slowCalls + parms->copiousCalls;
412 int t = (interval / totalCalls) * 1000.0 + 0.5;
413 if (totalCalls > 0) {
414 printf("For %lu calls: %d msec/call\n", totalCalls, t);
415 }
416 if (parms->copiousCalls > 0) {
417 long n = parms->sendLen + parms->recvLen;
418 double kbps;
419 int b;
420 kbps = (double)(parms->copiousCalls * n) / (interval * 1000.0);
421 b = kbps + 0.5;
422 #if 0
423 I just cannot get printing of floats to work on the pmax !
424 !!!printf("%g %d %d %d\n", (float)kbps, b);
425 printf("%g %d %d %d\n", kbps, b);
426 fprintf(stdout, "%g %d %d\n", kbps, b);
427 {
428 char buf[100];
429 buf[sizeof(buf) - 1] = 0;
430 sprintf(buf, "%g %d %d\n", kbps, b);
431 assert(buf[sizeof(buf) - 1] == 0);
432 printf("%s", buf);
433 }
434 #endif
435 printf
436 ("For %lu copious calls, %lu send + %lu recv = %lu bytes each: %d kbytes/sec\n",
437 parms->copiousCalls, parms->sendLen, parms->recvLen, n, b);
438 #if 0
439 printf("%g\n", kbps);
440 #endif
441 }
442 }
443 return 0;
444 }
445
446 static long
447 RepeatLoadTest(struct clientParms *parms, struct rx_connection *conn)
448 {
449 long code;
450 long count;
451
452 if (parms->repeatInterval == 0) {
453 if (parms->repeatCount == 0)
454 parms->repeatCount = 1;
455 } else {
456 if (parms->repeatCount == 0)
457 parms->repeatCount = 0x7fffffff;
458 }
459
460 if (parms->printTiming) {
461 int types;
462 types =
463 (parms->fastCalls ? 1 : 0) + (parms->slowCalls ? 1 : 0) +
464 (parms->copiousCalls ? 1 : 0);
465 if (types > 1)
466 fprintf(stderr,
467 "Combined timings of several types of calls may not be meaningful.\n");
468 if (types == 0)
469 /* do timings of copious calls by default */
470 parms->copiousCalls = 10;
471 }
472
473 for (count = 0; count < parms->repeatCount; count++) {
474 code = RunLoadTest(parms, conn);
475 if (code)
476 return code;
477 if (parms->repeatInterval) {
478 u_long i = parms->repeatInterval;
479 u_long now = time(0);
480 u_long next = (now + i - 1) / i * i; /* round up to next interval */
481 while (now < next) {
482 #ifdef AFS_PTHREAD_ENV
483 sleep(next - now);
484 #else
485 IOMGR_Sleep(next - now);
486 #endif
487 now = time(0);
488 }
489 }
490 }
491 return code;
492 }
493
494 /* For backward compatibility, don't try to use the CallNumber stuff unless
495 * we're compiling against the new Rx. */
496
497 #ifdef rx_GetPacketCksum
498
499 struct multiChannel {
500 struct rx_connection *conn;
501 int done;
502 long *codes;
503 int changes[RX_MAXCALLS];
504 afs_int32 callNumbers[RX_MAXCALLS];
505 };
506 #define BIG_PRIME 1257056893 /* 0x4AED2A7d */
507 static u_long sequence = 0;
508
509 static long
510 FastCall(struct rx_connection *conn)
511 {
512 long code;
513 u_long n = (sequence = sequence * BIG_PRIME + BIG_PRIME);
514 u_long inc_n;
515
516 code = RXKST_Fast(conn, n, &inc_n);
517 if (code)
518 return code;
519 if (inc_n != n + 1)
520 return RXKST_INCFAILED;
521 return 0;
522 }
523
524 static long
525 UniChannelCall(int index, opaque rock)
526 {
527 struct multiChannel *mc = (struct multiChannel *)rock;
528 long code;
529 afs_int32 callNumbers[RX_MAXCALLS];
530 int unchanged;
531
532 code = 0;
533 unchanged = 1;
534 while (!mc->done && unchanged) {
535 int i;
536 code = FastCall(mc->conn);
537 if (code)
538 break;
539 code = rxi_GetCallNumberVector(mc->conn, callNumbers);
540 if (code)
541 break;
542 unchanged = 0;
543 for (i = 0; i < RX_MAXCALLS; i++) {
544 if (callNumbers[i] > mc->callNumbers[i]) {
545 mc->callNumbers[i] = callNumbers[i];
546 mc->changes[i]--; /* may go negative */
547 }
548 if (mc->changes[i] > 0)
549 unchanged++;
550 }
551 }
552 mc->codes[index] = code;
553 mc->done++;
554 return code;
555 }
556
557 static long
558 MakeMultiChannelCall(struct rx_connection *conn, int each,
559 long expectedCode, long codes[])
560 {
561 long code;
562 int i;
563 struct multiChannel mc;
564
565 memset(&mc, 0, sizeof(mc));
566 mc.conn = conn;
567 for (i = 0; i < RX_MAXCALLS; i++) {
568 codes[i] = RXKST_PROCESSRUNNING;
569 mc.changes[i] = each;
570 }
571 mc.codes = codes;
572 code = rxi_GetCallNumberVector(conn, mc.callNumbers);
573 if (code)
574 return code;
575 mc.done = 0;
576 code = CallSimultaneously(RX_MAXCALLS, &mc, UniChannelCall);
577 if (((expectedCode == RXKST_INCFAILED) || (expectedCode == -1)) && ((code == expectedCode) || (code == -3))); /* strange cases */
578 else if (code != expectedCode) {
579 afs_com_err(whoami, code,
580 "problem making multichannel call, expected '%s'",
581 ((expectedCode == 0)
582 ? "no error" : (char *)afs_error_message(expectedCode)));
583 }
584 return code;
585 }
586
587 static long
588 CheckCallFailure(struct rx_connection *conn, long codes[], long code,
589 char *msg)
590 {
591 if (code == 0) {
592 fprintf(stderr, "Failed to detect %s\n", msg);
593 return RXKST_NODUPLICATECALL;
594 } else {
595 int i;
596 int okay = 1;
597 int someZero = 0;
598 for (i = 0; i < RX_MAXCALLS; i++)
599 if (!((codes[i] == 0) || (codes[i] == code) || (codes[i] == -3)))
600 okay = 0;
601 if (conn->error)
602 okay = 0;
603 if (!okay) {
604 fprintf(stderr, "%s produced these errors:\n", msg);
605 for (i = 0; i < RX_MAXCALLS; i++) {
606 assert(codes[i] != RXKST_PROCESSRUNNING);
607 if (codes[i] == 0) {
608 someZero++;
609 fprintf(stderr, " %d no error\n", i);
610 } else
611 fprintf(stderr, " %d %s\n", i, afs_error_message(codes[i]));
612 }
613 if (someZero) {
614 char buf[100];
615 sprintf(buf, "connection dead following %s", msg);
616 code = FastCall(conn);
617 if (code)
618 afs_com_err(whoami, code, "%s", buf);
619 }
620 }
621 }
622 return 0;
623 }
624
625 #endif /* rx_GetPacketCksum */
626
627 static long
628 RunCallTest(struct clientParms *parms, long host,
629 struct rx_securityClass *sc, long si)
630 {
631 long code;
632
633 #ifndef rx_GetPacketCksum
634
635 code = RXKST_BADARGS;
636 afs_com_err(whoami, code,
637 "Older versions of Rx don't support Get/Set callNumber Vector procedures: can't run this CallTest");
638 return code;
639
640 #else
641
642 int i, ch;
643 struct rx_connection *conn;
644 long firstCall;
645 afs_int32 callNumbers[RX_MAXCALLS];
646 long codes[RX_MAXCALLS];
647 long retCode = 0; /* ret. if nothing fatal goes wrong */
648
649 conn =
650 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
651 si);
652 if (!conn)
653 return RXKST_NEWCONNFAILED;
654
655 /* First check the basic behaviour of call number handling */
656
657 code = rxi_GetCallNumberVector(conn, callNumbers);
658 if (code)
659 return code;
660 for (i = 0; i < RX_MAXCALLS; i++) {
661 if (callNumbers[i] != 0) {
662 fprintf(stderr,
663 "Connection's initial call numbers not zero. call[%d] = %d\n",
664 i, callNumbers[i]);
665 return RXKST_BADCALLNUMBERS;
666 }
667 }
668 code = FastCall(conn);
669 if (code)
670 return code;
671 code = rxi_GetCallNumberVector(conn, callNumbers);
672 if (code)
673 return code;
674 firstCall = callNumbers[0];
675 code = FastCall(conn);
676 if (code)
677 return code;
678 code = rxi_GetCallNumberVector(conn, callNumbers);
679 if (code)
680 return code;
681 if ((callNumbers[0] != firstCall + 1)
682 && ((firstCall == 1) || (firstCall == 2))) {
683 /* The call number after the first call should be one or, more likely,
684 * two (if the call is still DALLYing). Between first and second call,
685 * the call number should have incremented by one. */
686 fprintf(stderr,
687 "Connection's first channel call number not one. call[%d] = %d\n",
688 0, callNumbers[0]);
689 return RXKST_BADCALLNUMBERS;
690 }
691 for (i = 1; i < RX_MAXCALLS; i++) {
692 if (callNumbers[i] != 0) {
693 fprintf(stderr,
694 "Connection's other channel call numbers not zero. call[%d] = %d\n",
695 i, callNumbers[i]);
696 return RXKST_BADCALLNUMBERS;
697 }
698 }
699 code = MakeMultiChannelCall(conn, 1, 0, codes);
700 if (code)
701 return code;
702
703 /* Now try to resend a call that's already been executed by finding a
704 * non-zero call number on a channel other than zero and decrementing it by
705 * one. This should appear to the server as a retransmitted call. Since
706 * this is behaving as a broken client different strange behaviors may be
707 * exhibited by different servers. If the response packet to the original
708 * call is discarded by the time the "retransmitted" call arrives (perhaps
709 * due to high server or client load) there is no way for the server to
710 * respond at all. Further, it seems, that under some cases the connection
711 * will be kept alive indefinitely even though the server has discarded the
712 * "retransmitted" call and is making no effort to reexecute the call. To
713 * handle these, accept either a timeout (-1) or and INCFAILED error here,
714 * also set the connenction HardDeadTime to punt after a reasonable
715 * interval. */
716
717 /* short dead time since may we expect some trouble */
718 rx_SetConnHardDeadTime(conn, 30);
719 code = rxi_GetCallNumberVector(conn, callNumbers);
720 if (code)
721 return code;
722 for (ch = 1; ch < RX_MAXCALLS; ch++)
723 if (callNumbers[ch] > 1) {
724 callNumbers[ch]--;
725 code = rxi_SetCallNumberVector(conn, callNumbers);
726 if (code)
727 return code;
728 break;
729 }
730 if (ch >= RX_MAXCALLS) /* didn't find any? all DALLYing? */
731 return RXKST_BADCALLNUMBERS;
732 code = MakeMultiChannelCall(conn, 1, RXKST_INCFAILED, codes);
733 code = CheckCallFailure(conn, codes, code, "retransmitted call");
734 if (code && !retCode)
735 retCode = code;
736
737 /* Get a fresh connection, becasue if the above failed as it should the
738 * connection is dead. */
739 rx_DestroyConnection(conn);
740 conn =
741 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
742 si);
743 if (!conn)
744 return RXKST_NEWCONNFAILED;
745
746 /* Similarly, but decrement call number by two which should be completely
747 * unmistakeable as a broken or malicious client. */
748
749 /* short dead time since may we expect some trouble */
750 rx_SetConnHardDeadTime(conn, 30);
751 code = MakeMultiChannelCall(conn, 2, 0, codes);
752 if (code)
753 return code;
754 code = rxi_GetCallNumberVector(conn, callNumbers);
755 if (code)
756 return code;
757 for (ch = 1; ch < RX_MAXCALLS; ch++)
758 if (callNumbers[ch] > 2) {
759 callNumbers[ch] -= 2;
760 code = rxi_SetCallNumberVector(conn, callNumbers);
761 break;
762 }
763 if (ch >= RX_MAXCALLS) /* didn't find any? all DALLYing? */
764 return RXKST_BADCALLNUMBERS;
765 code = MakeMultiChannelCall(conn, 1, -1, codes);
766 code = CheckCallFailure(conn, codes, code, "duplicate call");
767 if (code && !retCode)
768 retCode = code;
769
770 rx_DestroyConnection(conn);
771 conn =
772 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID, sc,
773 si);
774 if (!conn)
775 return RXKST_NEWCONNFAILED;
776
777 /* Next, without waiting for the server to discard its state, we will check
778 * to see if the Challenge/Response protocol correctly informs the server
779 * of the client's callNumber state. We do this by artificially increasing
780 * the call numbers of a new connection for all channels beyond zero,
781 * making a call on channel zero, then resetting the call number for the
782 * unused channels back to zero, then making calls on all channels. */
783
784 code = rxi_GetCallNumberVector(conn, callNumbers);
785 if (code)
786 return code;
787 for (i = 0; i < RX_MAXCALLS; i++) {
788 if (callNumbers[i] != 0)
789 return RXKST_BADCALLNUMBERS;
790 callNumbers[i] = 51; /* an arbitrary value... */
791 }
792 code = rxi_SetCallNumberVector(conn, callNumbers);
793 if (code)
794 return code;
795 code = FastCall(conn); /* use channel 0 */
796 if (code)
797 return code;
798 code = rxi_GetCallNumberVector(conn, callNumbers);
799 if (code)
800 return code;
801 if (callNumbers[0] != 52)
802 return RXKST_BADCALLNUMBERS;
803 for (i = 1; i < RX_MAXCALLS; i++) {
804 if (callNumbers[i] != 51)
805 return RXKST_BADCALLNUMBERS;
806 callNumbers[i] = 37; /* back up a ways */
807 }
808 code = rxi_SetCallNumberVector(conn, callNumbers);
809 if (code)
810 return code;
811 /* now try calls on all channels... */
812 code = MakeMultiChannelCall(conn, 1, -1, codes);
813 code =
814 CheckCallFailure(conn, codes, code, "alternate channel call replay");
815 if (code && !retCode)
816 retCode = code;
817
818 rx_DestroyConnection(conn);
819 return retCode;
820
821 #endif /* rx_GetPacketCksum */
822
823 }
824
825 #ifdef rx_GetPacketCksum
826
827 static struct {
828 int op;
829 u_long epoch; /* connection to attack */
830 u_long cid;
831 int client; /* TRUE => client side */
832 u_long newEpoch; /* conn to direct challenges to */
833 u_long newCid;
834 u_long counts[RX_N_PACKET_TYPES];
835 } incomingOps;
836 #define IO_NOOP 0
837 #define IO_COUNT 1
838 #define IO_REDIRECTCHALLENGE 2
839
840 static int
841 HandleIncoming(struct rx_packet *p, struct sockaddr_in *addr)
842 {
843 int client; /* packet sent by client */
844 u_char type; /* packet type */
845
846 if (incomingOps.op == IO_NOOP)
847 return 0;
848
849 client = ((p->header.flags & RX_CLIENT_INITIATED) != RX_CLIENT_INITIATED);
850 if ((p->header.epoch != incomingOps.epoch)
851 || ((p->header.cid ^ incomingOps.cid) & RX_CIDMASK)
852 || (client != incomingOps.client))
853 return 0;
854 type = p->header.type;
855 if ((type <= 0) || (type >= RX_N_PACKET_TYPES))
856 type = 0;
857 incomingOps.counts[type]++;
858
859 switch (incomingOps.op) {
860 case IO_NOOP:
861 case IO_COUNT:
862 break;
863
864 case IO_REDIRECTCHALLENGE:
865 if (p->header.type != RX_PACKET_TYPE_CHALLENGE)
866 break;
867 p->header.epoch = incomingOps.newEpoch;
868 p->header.cid = incomingOps.newCid;
869 /* Now set up to watch for the corresponding challenge. */
870 incomingOps.epoch = incomingOps.newEpoch;
871 incomingOps.cid = incomingOps.newCid;
872 incomingOps.op = IO_COUNT;
873 break;
874
875 default:
876 fprintf(stderr, "Unknown incoming op %d\n", incomingOps.op);
877 break;
878 }
879 return 0;
880 }
881
882 static struct {
883 int op;
884 u_long epoch; /* connection to attack */
885 u_long cid;
886 int client; /* TRUE => client side */
887 u_long counts[RX_N_PACKET_TYPES];
888 } outgoingOps;
889 #define OO_NOOP 0
890 #define OO_COUNT 1
891 #define OO_ZEROCKSUM 2
892 #define OO_MUNGCKSUM 3
893
894 static int
895 HandleOutgoing(struct rx_packet *p, struct sockaddr_in *addr)
896 {
897 int client; /* packet sent by client */
898 u_char type; /* packet type */
899
900 if (outgoingOps.op == OO_NOOP)
901 return 0;
902
903 client = ((p->header.flags & RX_CLIENT_INITIATED) == RX_CLIENT_INITIATED);
904 if ((p->header.epoch != outgoingOps.epoch)
905 || ((p->header.cid ^ outgoingOps.cid) & RX_CIDMASK)
906 || (client != outgoingOps.client))
907 return 0;
908 type = p->header.type;
909 if ((type <= 0) || (type >= RX_N_PACKET_TYPES))
910 type = 0;
911 outgoingOps.counts[type]++;
912
913 switch (outgoingOps.op) {
914 case OO_NOOP:
915 case OO_COUNT:
916 /* counting always happens above if not noop */
917 break;
918
919 case OO_ZEROCKSUM:
920 if (p->header.type != RX_PACKET_TYPE_DATA)
921 break;
922 if (rx_GetPacketCksum(p) == 0) {
923 /* probably, a retransmitted packet */
924 fprintf(stderr, "Packet cksum already zero\n");
925 break;
926 }
927 rx_SetPacketCksum(p, 0);
928 break;
929
930 case OO_MUNGCKSUM:{
931 u_short cksum;
932 if (p->header.type != RX_PACKET_TYPE_DATA)
933 break;
934 cksum = rx_GetPacketCksum(p);
935 if (cksum == 0) {
936 fprintf(stderr, "Packet cksum already zero\n");
937 break;
938 }
939 rx_SetPacketCksum(p, cksum ^ 8);
940 break;
941 }
942 default:
943 fprintf(stderr, "Unknown outgoing op %d\n", outgoingOps.op);
944 break;
945 }
946 return 0;
947 }
948
949 #ifdef AFS_PTHREAD_ENV
950 static pthread_once_t slowCallOnce = PTHREAD_ONCE_INIT;
951 static pthread_mutex_t slowCallLock;
952 static pthread_cond_t slowCallCV;
953 void
954 SlowCallInit(void)
955 {
956 pthread_mutex_init(&slowCallLock, NULL);
957 pthread_cond_init(&slowCallCV, NULL);
958 }
959 #endif
960 static long slowCallCode;
961 static void *
962 SlowCall(void * rock)
963 {
964 struct rx_connection *conn = rock;
965 u_long ntime;
966 u_long now;
967 long temp_rc;
968
969 #ifdef AFS_PTHREAD_ENV
970 pthread_mutex_lock(&slowCallLock);
971 #endif
972 slowCallCode = RXKST_PROCESSRUNNING;
973 #ifdef AFS_PTHREAD_ENV
974 pthread_cond_signal(&slowCallCV);
975 #else
976 LWP_NoYieldSignal(&slowCallCode);
977 #endif
978 slowCallCode = RXKST_Slow(conn, 1, &ntime);
979 if (!slowCallCode) {
980 now = FT_ApproxTime();
981 if ((ntime < now - maxSkew) || (ntime > now + maxSkew))
982 slowCallCode = RXKST_TIMESKEW;
983 }
984 temp_rc = slowCallCode;
985 #ifdef AFS_PTHREAD_ENV
986 pthread_cond_signal(&slowCallCV);
987 pthread_mutex_unlock(&slowCallLock);
988 #else
989 LWP_NoYieldSignal(&slowCallCode);
990 #endif
991 return (void *)(intptr_t)temp_rc;
992 }
993
994 #endif /* rx_GetPacketCksum */
995
996 static long
997 RunHijackTest(struct clientParms *parms, long host,
998 struct rx_securityClass *sc, long si)
999 {
1000
1001 #ifndef rx_GetPacketCksum
1002
1003 code = RXKST_BADARGS;
1004 afs_com_err(whoami, code,
1005 "Older versions of Rx don't export packet tracing routines: can't run this HijackTest");
1006 return code;
1007
1008 #else
1009
1010 long code;
1011 struct rx_connection *conn = 0;
1012 struct rx_connection *otherConn = 0;
1013 #ifdef AFS_PTHREAD_ENV
1014 pthread_t pid;
1015 #else
1016 PROCESS pid;
1017 #endif
1018 int nResp; /* otherConn responses seen */
1019 long tmp_rc;
1020
1021 #ifdef AFS_PTHREAD_ENV
1022 pthread_once(&slowCallOnce, SlowCallInit);
1023 #endif
1024 rx_justReceived = HandleIncoming;
1025 rx_almostSent = HandleOutgoing;
1026
1027 incomingOps.op = IO_NOOP;
1028 outgoingOps.op = OO_NOOP;
1029
1030 #define HIJACK_CONN(conn) \
1031 { if (conn) rx_DestroyConnection (conn); \
1032 (conn) = rx_NewConnection(host, htons(RXKST_SERVICEPORT), \
1033 RXKST_SERVICEID, sc, si); \
1034 if (!(conn)) return RXKST_NEWCONNFAILED; \
1035 outgoingOps.client = 1; \
1036 outgoingOps.epoch = (conn)->epoch; \
1037 outgoingOps.cid = (conn)->cid; }
1038
1039 HIJACK_CONN(conn);
1040
1041 /* First try switching from no packet cksum to sending packet cksum between
1042 * calls, and see if server complains. */
1043
1044 outgoingOps.op = OO_ZEROCKSUM;
1045 code = FastCall(conn);
1046 if (code) {
1047 afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1048 return code;
1049 }
1050 /* The server thinks we're an old style client. Now start sending cksums.
1051 * Server shouldn't care. */
1052 outgoingOps.op = OO_NOOP;
1053 code = FastCall(conn);
1054 if (code) {
1055 afs_com_err(whoami, code, "doing FastCall with non-ZEROCKSUM");
1056 return code;
1057 }
1058 /* The server now thinks we're a new style client, we can't go back now. */
1059 outgoingOps.op = OO_ZEROCKSUM;
1060 code = FastCall(conn);
1061 if (code == 0)
1062 code = RXKST_NOBADCKSUM;
1063 if (code != RXKADSEALEDINCON) {
1064 afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1065 return code;
1066 } else if (!conn->error) {
1067 code = RXKST_NOCONNERROR;
1068 afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1069 return code;
1070 } else
1071 code = 0;
1072
1073 HIJACK_CONN(conn);
1074
1075 /* Now try modifying packet cksum to see if server complains. */
1076
1077 outgoingOps.op = OO_MUNGCKSUM;
1078 code = FastCall(conn);
1079 if (code == 0)
1080 code = RXKST_NOBADCKSUM;
1081 if (code != RXKADSEALEDINCON) {
1082 afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1083 return code;
1084 } else if (!conn->error) {
1085 code = RXKST_NOCONNERROR;
1086 afs_com_err(whoami, code, "doing FastCall with ZEROCKSUM");
1087 return code;
1088 } else
1089 code = 0;
1090
1091 /* Now make two connection and direct the first challenge on one connection
1092 * to the other connection to see if it generates a response. The
1093 * retransmitted challenge should allow the call on the first connection to
1094 * complete correctly. Part one is to attack a new connection, then attack
1095 * it after it has made a call. Part three, just for comparison, attacks a
1096 * otherConn while it is making a slow call (and thus has an active call).
1097 * Against this attack we have no defense so we expect a challenge in this
1098 * case, which the server will discard. */
1099
1100 #define RedirectChallenge(conn,otherConn) \
1101 (incomingOps.epoch = (conn)->epoch, \
1102 incomingOps.cid = (conn)->cid, \
1103 incomingOps.client = 1, \
1104 incomingOps.newEpoch = (otherConn)->epoch, \
1105 incomingOps.newCid = (otherConn)->cid, \
1106 incomingOps.op = IO_REDIRECTCHALLENGE, \
1107 outgoingOps.epoch = (otherConn)->epoch, \
1108 outgoingOps.cid = (otherConn)->cid, \
1109 outgoingOps.client = 1, \
1110 outgoingOps.op = OO_COUNT, \
1111 outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] = 0)
1112
1113 HIJACK_CONN(conn);
1114 HIJACK_CONN(otherConn)
1115 RedirectChallenge(conn, otherConn);
1116
1117 code = FastCall(conn);
1118 if (code)
1119 return code;
1120 assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1121 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > 0) {
1122 oracle:
1123 code = RXKST_CHALLENGEORACLE;
1124 afs_com_err(whoami, code, "misdirecting challenge");
1125 return code;
1126 }
1127 code = FastCall(otherConn); /* generate some activity here */
1128 if (code)
1129 return code;
1130 nResp = outgoingOps.counts[RX_PACKET_TYPE_RESPONSE];
1131 assert(nResp >= 1);
1132 code = FastCall(conn);
1133 if (code)
1134 return code;
1135 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] > nResp)
1136 goto oracle;
1137
1138 HIJACK_CONN(conn);
1139 RedirectChallenge(conn, otherConn);
1140 /* otherConn was authenticated during part one */
1141 code = FastCall(conn);
1142 if (code)
1143 return code;
1144 assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1145 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 0)
1146 goto oracle;
1147
1148 HIJACK_CONN(conn);
1149 RedirectChallenge(conn, otherConn);
1150 /* otherConn is still authenticated */
1151 slowCallCode = RXKST_PROCESSCREATED;
1152 #ifdef AFS_PTHREAD_ENV
1153 {
1154 pthread_attr_t tattr;
1155
1156 code = pthread_attr_init(&tattr);
1157 if (code) {
1158 afs_com_err(whoami, code,
1159 "can't pthread_attr_init slow call process");
1160 return code;
1161 }
1162
1163 code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1164 if (code) {
1165 afs_com_err(whoami, code,
1166 "can't pthread_attr_setdetachstate slow call process");
1167 return code;
1168 }
1169
1170 code = pthread_create(&pid, &tattr, SlowCall, (void *)otherConn);
1171 }
1172 #else
1173 code =
1174 LWP_CreateProcess(SlowCall, 16000, LWP_NORMAL_PRIORITY,
1175 (opaque) otherConn, "Slow Call Process", &pid);
1176 #endif
1177 if (code) {
1178 afs_com_err(whoami, code, "can't create slow call process");
1179 return code;
1180 }
1181 #ifdef AFS_PTHREAD_ENV
1182 pthread_mutex_lock(&slowCallLock);
1183 while (slowCallCode == RXKST_PROCESSCREATED)
1184 pthread_cond_wait(&slowCallCV, &slowCallLock);
1185 #else
1186 while (slowCallCode == RXKST_PROCESSCREATED)
1187 LWP_WaitProcess(&slowCallCode); /* wait for process start */
1188 #endif
1189 if (slowCallCode != RXKST_PROCESSRUNNING) {
1190 tmp_rc = slowCallCode;
1191 #ifdef AFS_PTHREAD_ENV
1192 pthread_mutex_unlock(&slowCallLock);
1193 #endif
1194 return tmp_rc; /* make sure didn't fail immediately */
1195 }
1196 assert(incomingOps.op == IO_REDIRECTCHALLENGE);
1197 code = FastCall(conn);
1198 if (code)
1199 return code;
1200 assert(incomingOps.op == IO_COUNT); /* redirect code was triggered */
1201 #ifdef AFS_PTHREAD_ENV
1202 while (slowCallCode == RXKST_PROCESSRUNNING)
1203 pthread_cond_wait(&slowCallCV, &slowCallLock);
1204 pthread_mutex_unlock(&slowCallLock);
1205 #else
1206 while (slowCallCode == RXKST_PROCESSRUNNING)
1207 LWP_WaitProcess(&slowCallCode); /* wait for process finish */
1208 #endif
1209 if (outgoingOps.counts[RX_PACKET_TYPE_RESPONSE] != 1)
1210 goto oracle;
1211
1212 rx_justReceived = 0;
1213 rx_almostSent = 0;
1214 rx_DestroyConnection(otherConn);
1215 rx_DestroyConnection(conn);
1216 return code;
1217
1218 #endif /* rx_GetPacketCksum */
1219
1220 }
1221
1222 long
1223 rxkst_StartClient(struct clientParms *parms)
1224 {
1225 long code;
1226 long host;
1227 long scIndex;
1228 struct rx_securityClass *sc;
1229
1230 whoami = parms->whoami; /* set this global variable */
1231
1232 host = GetServer(parms->server);
1233
1234 if (parms->authentication >= 0) {
1235 long kvno = 0;
1236 char ticket[MAXKTCTICKETLEN];
1237 int ticketLen;
1238 struct ktc_encryptionKey Ksession;
1239
1240 if (parms->useTokens)
1241 code =
1242 GetToken(&kvno, &Ksession, &ticketLen, ticket, parms->cell);
1243 else
1244 code =
1245 GetTicket(&kvno, &Ksession, &ticketLen, ticket, parms->cell);
1246 if (code)
1247 return code;
1248
1249 /* next, we have ticket, kvno and session key, authenticate the conn */
1250 sc = (struct rx_securityClass *)
1251 rxkad_NewClientSecurityObject(parms->authentication, &Ksession,
1252 kvno, ticketLen, ticket);
1253 assert(sc);
1254 scIndex = RX_SECIDX_KAD;
1255 } else {
1256 /* unauthenticated connection */
1257 sc = rxnull_NewClientSecurityObject();
1258 assert(sc);
1259 scIndex = RX_SECIDX_NULL;
1260 }
1261
1262 code = 0;
1263 if (!code && parms->callTest) {
1264 code = RunCallTest(parms, host, sc, scIndex);
1265 }
1266 if (!code && parms->hijackTest) {
1267 code = RunHijackTest(parms, host, sc, scIndex);
1268 }
1269 if (!code
1270 && (parms->printTiming || parms->fastCalls || parms->slowCalls
1271 || parms->copiousCalls)) {
1272 struct rx_connection *conn;
1273 conn =
1274 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
1275 sc, scIndex);
1276 if (conn) {
1277 code = RepeatLoadTest(parms, conn);
1278 rx_DestroyConnection(conn);
1279 } else
1280 code = RXKST_NEWCONNFAILED;
1281 }
1282 if (!code && parms->stopServer) {
1283 struct rx_connection *conn;
1284 conn =
1285 rx_NewConnection(host, htons(RXKST_SERVICEPORT), RXKST_SERVICEID,
1286 sc, scIndex);
1287 if (conn) {
1288 code = RXKST_Kill(conn);
1289 if (code) {
1290 afs_com_err(whoami, code, "trying to stop server");
1291 }
1292 rx_DestroyConnection(conn);
1293 } else
1294 code = RXKST_NEWCONNFAILED;
1295 }
1296
1297 if (parms->printStats) {
1298 rx_PrintStats(stdout);
1299 #if 0
1300 /* use rxdebug style iteration here */
1301 rx_PrintPeerStats(stdout, rx_PeerOf(conn));
1302 #endif
1303 }
1304
1305 rxs_Release(sc);
1306 rx_Finalize();
1307 if (code) {
1308 afs_com_err(parms->whoami, code, "test fails");
1309 exit(13);
1310 } else {
1311 printf("Test Okay\n");
1312 if (!parms->noExit)
1313 exit(0);
1314 }
1315 return 0;
1316 }