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