2 * Copyright 2000, International Business Machines Corporation and others.
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
10 /* RX Authentication Stress test: client side code. */
12 #include <afsconfig.h>
13 #include <afs/param.h>
19 #include <afs/com_err.h>
20 #include <afs/afsutil.h>
23 #include <des_prototypes.h>
25 #include "stress_internal.h"
26 #ifdef AFS_PTHREAD_ENV
28 #define FT_ApproxTime() (int)time(0)
36 GetServer(const char *aname
)
41 th
= gethostbyname(aname
);
43 fprintf(stderr
, "host %s not found\n", aname
);
46 memcpy(&addr
, th
->h_addr
, sizeof(addr
));
51 GetToken(long *versionP
, struct ktc_encryptionKey
*session
,
52 int *ticketLenP
, char *ticket
, char *cell
)
54 struct ktc_principal sname
;
55 struct ktc_token ttoken
;
58 strcpy(sname
.cell
, cell
);
59 sname
.instance
[0] = 0;
60 strcpy(sname
.name
, "afs");
61 code
= ktc_GetToken(&sname
, &ttoken
, sizeof(ttoken
), NULL
);
65 *versionP
= ttoken
.kvno
;
66 *ticketLenP
= ttoken
.ticketLen
;
67 memcpy(ticket
, ttoken
.ticket
, ttoken
.ticketLen
);
68 memcpy(session
, &ttoken
.sessionKey
, sizeof(struct ktc_encryptionKey
));
73 GetTicket(long *versionP
, struct ktc_encryptionKey
*session
,
74 int *ticketLenP
, char *ticket
, char *cell
)
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
));
84 /* now create the actual ticket */
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 */
96 *versionP
= serviceKeyVersion
;
100 struct rx_connection
*conn
;
101 u_long sendLen
; /* parameters for call to Copious */
103 u_long
*fastCalls
; /* number of calls to perform */
105 u_long
*copiousCalls
;
109 Copious(struct client
*c
, char *buf
, u_long buflen
)
112 struct rx_call
*call
;
114 long inlen
= c
->sendLen
;
115 long outlen
= c
->recvLen
;
121 for (i
= 0; i
< inlen
; i
++)
122 mysum
+= (d
++ & 0xff);
124 call
= rx_NewCall(c
->conn
);
125 code
= StartRXKST_Copious(call
, inlen
, mysum
, outlen
);
131 while (xfer
< inlen
) {
135 for (i
= 0; i
< tlen
; i
++)
136 buf
[i
] = (d
++ & 0xff);
137 n
= rx_Write(call
, buf
, tlen
);
142 code
= RXKST_WRITESHORT
;
150 while (xfer
< outlen
) {
151 tlen
= outlen
- xfer
;
154 n
= rx_Read(call
, buf
, tlen
);
159 code
= RXKST_READSHORT
;
162 for (i
= 0; i
< tlen
; i
++)
169 code
= EndRXKST_Copious(call
, &outsum
);
170 code
= rx_EndCall(call
, code
);
173 if (outsum
!= mysum
) {
174 return RXKST_BADOUTPUTSUM
;
180 DoClient(int index
, opaque rock
)
182 struct client
*c
= (struct client
*)rock
;
188 for (i
= 0; i
< c
->fastCalls
[index
]; i
++) {
189 code
= RXKST_Fast(c
->conn
, n
, &inc_n
);
193 return RXKST_INCFAILED
;
197 for (i
= 0; i
< c
->slowCalls
[index
]; i
++) {
200 code
= RXKST_Slow(c
->conn
, 1, &ntime
);
203 now
= FT_ApproxTime();
204 if ((ntime
< now
- maxSkew
) || (ntime
> now
+ maxSkew
))
205 return RXKST_TIMESKEW
;
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
);
216 osi_Free(buf
, buflen
);
225 long exitCode
; /* is PROCESSRUNNING until exit */
228 long (*proc
) (int, opaque
);
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
;
238 pthread_mutex_init(&workerLock
, NULL
);
239 pthread_cond_init(&workerCV
, NULL
);
242 static struct worker
*workers
;
245 DoWorker(void * rock
)
247 struct worker
*w
= rock
;
249 code
= (*w
->proc
) (w
->index
, w
->rock
);
250 #ifdef AFS_PTHREAD_ENV
251 pthread_mutex_lock(&workerLock
);
254 #ifdef AFS_PTHREAD_ENV
255 pthread_mutex_unlock(&workerLock
);
257 #ifdef AFS_PTHREAD_ENV
258 pthread_cond_signal(&workerCV
);
260 LWP_NoYieldSignal(&workers
);
262 return (void *)(intptr_t)code
;
265 #define MAX_CTHREADS 25
268 CallSimultaneously(u_int threads
, opaque rock
, long (*proc
)(int, opaque
))
272 #ifdef AFS_PTHREAD_ENV
273 pthread_once(&workerOnce
, WorkerInit
);
277 for (i
= 0; i
< threads
; i
++) {
279 #ifdef AFS_PTHREAD_ENV
284 assert(i
< MAX_CTHREADS
);
285 w
= osi_Alloc(sizeof(struct worker
));
286 memset(w
, 0, sizeof(*w
));
290 w
->exitCode
= RXKST_PROCESSRUNNING
;
293 #ifdef AFS_PTHREAD_ENV
295 pthread_attr_t tattr
;
297 code
= pthread_attr_init(&tattr
);
299 afs_com_err(whoami
, code
,
300 "can't pthread_attr_init worker process");
305 pthread_attr_setdetachstate(&tattr
, PTHREAD_CREATE_DETACHED
);
307 afs_com_err(whoami
, code
,
308 "can't pthread_attr_setdetachstate worker process");
312 code
= pthread_create(&pid
, &tattr
, DoWorker
, (void *)w
);
316 LWP_CreateProcess(DoWorker
, 16000, LWP_NORMAL_PRIORITY
,
317 (opaque
) w
, "Worker Process", &pid
);
320 afs_com_err(whoami
, code
, "can't create worker process");
324 code
= 0; /* last non-zero code encountered */
325 #ifdef AFS_PTHREAD_ENV
326 pthread_mutex_lock(&workerLock
);
329 struct worker
*w
, *prevW
, *nextW
;
331 for (w
= workers
; w
; w
= nextW
) {
333 if (w
->exitCode
!= RXKST_PROCESSRUNNING
) {
339 prevW
->next
= w
->next
;
342 osi_Free(w
, sizeof(*w
));
343 continue; /* don't bump prevW */
347 #ifdef AFS_PTHREAD_ENV
349 pthread_cond_wait(&workerCV
, &workerLock
);
352 LWP_WaitProcess(&workers
);
355 #ifdef AFS_PTHREAD_ENV
356 pthread_mutex_unlock(&workerLock
);
362 DivideUpCalls(u_long calls
, u_int threads
, u_long threadCalls
[])
365 for (i
= 0; i
< threads
; i
++) {
366 threadCalls
[i
] = calls
/ (threads
- i
);
367 calls
-= threadCalls
[i
];
375 gettimeofday(&tv
, NULL
);
376 return (double)tv
.tv_sec
+ (double)tv
.tv_usec
/ 1000000.0;
380 RunLoadTest(struct clientParms
*parms
, struct rx_connection
*conn
)
384 u_long fastCalls
[MAX_CTHREADS
];
385 u_long slowCalls
[MAX_CTHREADS
];
386 u_long copiousCalls
[MAX_CTHREADS
];
387 double start
, interval
;
389 DivideUpCalls(parms
->fastCalls
, parms
->threads
, fastCalls
);
390 DivideUpCalls(parms
->slowCalls
, parms
->threads
, slowCalls
);
391 DivideUpCalls(parms
->copiousCalls
, parms
->threads
, copiousCalls
);
393 memset(&c
, 0, sizeof(c
));
395 c
.sendLen
= parms
->sendLen
;
396 c
.recvLen
= parms
->recvLen
;
397 c
.fastCalls
= fastCalls
;
398 c
.slowCalls
= slowCalls
;
399 c
.copiousCalls
= copiousCalls
;
402 code
= CallSimultaneously(parms
->threads
, &c
, DoClient
);
404 afs_com_err(whoami
, code
, "in DoClient");
407 interval
= ftime() - start
;
409 if (parms
->printTiming
) {
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
);
416 if (parms
->copiousCalls
> 0) {
417 long n
= parms
->sendLen
+ parms
->recvLen
;
420 kbps
= (double)(parms
->copiousCalls
* n
) / (interval
* 1000.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
);
429 buf
[sizeof(buf
) - 1] = 0;
430 sprintf(buf
, "%g %d %d\n", kbps
, b
);
431 assert(buf
[sizeof(buf
) - 1] == 0);
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
);
439 printf("%g\n", kbps
);
447 RepeatLoadTest(struct clientParms
*parms
, struct rx_connection
*conn
)
452 if (parms
->repeatInterval
== 0) {
453 if (parms
->repeatCount
== 0)
454 parms
->repeatCount
= 1;
456 if (parms
->repeatCount
== 0)
457 parms
->repeatCount
= 0x7fffffff;
460 if (parms
->printTiming
) {
463 (parms
->fastCalls
? 1 : 0) + (parms
->slowCalls
? 1 : 0) +
464 (parms
->copiousCalls
? 1 : 0);
467 "Combined timings of several types of calls may not be meaningful.\n");
469 /* do timings of copious calls by default */
470 parms
->copiousCalls
= 10;
473 for (count
= 0; count
< parms
->repeatCount
; count
++) {
474 code
= RunLoadTest(parms
, conn
);
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 */
482 #ifdef AFS_PTHREAD_ENV
485 IOMGR_Sleep(next
- now
);
494 /* For backward compatibility, don't try to use the CallNumber stuff unless
495 * we're compiling against the new Rx. */
497 #ifdef rx_GetPacketCksum
499 struct multiChannel
{
500 struct rx_connection
*conn
;
503 int changes
[RX_MAXCALLS
];
504 afs_int32 callNumbers
[RX_MAXCALLS
];
506 #define BIG_PRIME 1257056893 /* 0x4AED2A7d */
507 static u_long sequence
= 0;
510 FastCall(struct rx_connection
*conn
)
513 u_long n
= (sequence
= sequence
* BIG_PRIME
+ BIG_PRIME
);
516 code
= RXKST_Fast(conn
, n
, &inc_n
);
520 return RXKST_INCFAILED
;
525 UniChannelCall(int index
, opaque rock
)
527 struct multiChannel
*mc
= (struct multiChannel
*)rock
;
529 afs_int32 callNumbers
[RX_MAXCALLS
];
534 while (!mc
->done
&& unchanged
) {
536 code
= FastCall(mc
->conn
);
539 code
= rxi_GetCallNumberVector(mc
->conn
, callNumbers
);
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 */
548 if (mc
->changes
[i
] > 0)
552 mc
->codes
[index
] = code
;
558 MakeMultiChannelCall(struct rx_connection
*conn
, int each
,
559 long expectedCode
, long codes
[])
563 struct multiChannel mc
;
565 memset(&mc
, 0, sizeof(mc
));
567 for (i
= 0; i
< RX_MAXCALLS
; i
++) {
568 codes
[i
] = RXKST_PROCESSRUNNING
;
569 mc
.changes
[i
] = each
;
572 code
= rxi_GetCallNumberVector(conn
, mc
.callNumbers
);
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'",
582 ? "no error" : (char *)afs_error_message(expectedCode
)));
588 CheckCallFailure(struct rx_connection
*conn
, long codes
[], long code
,
592 fprintf(stderr
, "Failed to detect %s\n", msg
);
593 return RXKST_NODUPLICATECALL
;
598 for (i
= 0; i
< RX_MAXCALLS
; i
++)
599 if (!((codes
[i
] == 0) || (codes
[i
] == code
) || (codes
[i
] == -3)))
604 fprintf(stderr
, "%s produced these errors:\n", msg
);
605 for (i
= 0; i
< RX_MAXCALLS
; i
++) {
606 assert(codes
[i
] != RXKST_PROCESSRUNNING
);
609 fprintf(stderr
, " %d no error\n", i
);
611 fprintf(stderr
, " %d %s\n", i
, afs_error_message(codes
[i
]));
615 sprintf(buf
, "connection dead following %s", msg
);
616 code
= FastCall(conn
);
618 afs_com_err(whoami
, code
, "%s", buf
);
625 #endif /* rx_GetPacketCksum */
628 RunCallTest(struct clientParms
*parms
, long host
,
629 struct rx_securityClass
*sc
, long si
)
633 #ifndef rx_GetPacketCksum
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");
643 struct rx_connection
*conn
;
645 afs_int32 callNumbers
[RX_MAXCALLS
];
646 long codes
[RX_MAXCALLS
];
647 long retCode
= 0; /* ret. if nothing fatal goes wrong */
650 rx_NewConnection(host
, htons(RXKST_SERVICEPORT
), RXKST_SERVICEID
, sc
,
653 return RXKST_NEWCONNFAILED
;
655 /* First check the basic behaviour of call number handling */
657 code
= rxi_GetCallNumberVector(conn
, callNumbers
);
660 for (i
= 0; i
< RX_MAXCALLS
; i
++) {
661 if (callNumbers
[i
] != 0) {
663 "Connection's initial call numbers not zero. call[%d] = %d\n",
665 return RXKST_BADCALLNUMBERS
;
668 code
= FastCall(conn
);
671 code
= rxi_GetCallNumberVector(conn
, callNumbers
);
674 firstCall
= callNumbers
[0];
675 code
= FastCall(conn
);
678 code
= rxi_GetCallNumberVector(conn
, callNumbers
);
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. */
687 "Connection's first channel call number not one. call[%d] = %d\n",
689 return RXKST_BADCALLNUMBERS
;
691 for (i
= 1; i
< RX_MAXCALLS
; i
++) {
692 if (callNumbers
[i
] != 0) {
694 "Connection's other channel call numbers not zero. call[%d] = %d\n",
696 return RXKST_BADCALLNUMBERS
;
699 code
= MakeMultiChannelCall(conn
, 1, 0, codes
);
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
717 /* short dead time since may we expect some trouble */
718 rx_SetConnHardDeadTime(conn
, 30);
719 code
= rxi_GetCallNumberVector(conn
, callNumbers
);
722 for (ch
= 1; ch
< RX_MAXCALLS
; ch
++)
723 if (callNumbers
[ch
] > 1) {
725 code
= rxi_SetCallNumberVector(conn
, callNumbers
);
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
)
737 /* Get a fresh connection, becasue if the above failed as it should the
738 * connection is dead. */
739 rx_DestroyConnection(conn
);
741 rx_NewConnection(host
, htons(RXKST_SERVICEPORT
), RXKST_SERVICEID
, sc
,
744 return RXKST_NEWCONNFAILED
;
746 /* Similarly, but decrement call number by two which should be completely
747 * unmistakeable as a broken or malicious client. */
749 /* short dead time since may we expect some trouble */
750 rx_SetConnHardDeadTime(conn
, 30);
751 code
= MakeMultiChannelCall(conn
, 2, 0, codes
);
754 code
= rxi_GetCallNumberVector(conn
, callNumbers
);
757 for (ch
= 1; ch
< RX_MAXCALLS
; ch
++)
758 if (callNumbers
[ch
] > 2) {
759 callNumbers
[ch
] -= 2;
760 code
= rxi_SetCallNumberVector(conn
, callNumbers
);
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
)
770 rx_DestroyConnection(conn
);
772 rx_NewConnection(host
, htons(RXKST_SERVICEPORT
), RXKST_SERVICEID
, sc
,
775 return RXKST_NEWCONNFAILED
;
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. */
784 code
= rxi_GetCallNumberVector(conn
, callNumbers
);
787 for (i
= 0; i
< RX_MAXCALLS
; i
++) {
788 if (callNumbers
[i
] != 0)
789 return RXKST_BADCALLNUMBERS
;
790 callNumbers
[i
] = 51; /* an arbitrary value... */
792 code
= rxi_SetCallNumberVector(conn
, callNumbers
);
795 code
= FastCall(conn
); /* use channel 0 */
798 code
= rxi_GetCallNumberVector(conn
, callNumbers
);
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 */
808 code
= rxi_SetCallNumberVector(conn
, callNumbers
);
811 /* now try calls on all channels... */
812 code
= MakeMultiChannelCall(conn
, 1, -1, codes
);
814 CheckCallFailure(conn
, codes
, code
, "alternate channel call replay");
815 if (code
&& !retCode
)
818 rx_DestroyConnection(conn
);
821 #endif /* rx_GetPacketCksum */
825 #ifdef rx_GetPacketCksum
829 u_long epoch
; /* connection to attack */
831 int client
; /* TRUE => client side */
832 u_long newEpoch
; /* conn to direct challenges to */
834 u_long counts
[RX_N_PACKET_TYPES
];
838 #define IO_REDIRECTCHALLENGE 2
841 HandleIncoming(struct rx_packet
*p
, struct sockaddr_in
*addr
)
843 int client
; /* packet sent by client */
844 u_char type
; /* packet type */
846 if (incomingOps
.op
== IO_NOOP
)
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
))
854 type
= p
->header
.type
;
855 if ((type
<= 0) || (type
>= RX_N_PACKET_TYPES
))
857 incomingOps
.counts
[type
]++;
859 switch (incomingOps
.op
) {
864 case IO_REDIRECTCHALLENGE
:
865 if (p
->header
.type
!= RX_PACKET_TYPE_CHALLENGE
)
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
;
876 fprintf(stderr
, "Unknown incoming op %d\n", incomingOps
.op
);
884 u_long epoch
; /* connection to attack */
886 int client
; /* TRUE => client side */
887 u_long counts
[RX_N_PACKET_TYPES
];
891 #define OO_ZEROCKSUM 2
892 #define OO_MUNGCKSUM 3
895 HandleOutgoing(struct rx_packet
*p
, struct sockaddr_in
*addr
)
897 int client
; /* packet sent by client */
898 u_char type
; /* packet type */
900 if (outgoingOps
.op
== OO_NOOP
)
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
))
908 type
= p
->header
.type
;
909 if ((type
<= 0) || (type
>= RX_N_PACKET_TYPES
))
911 outgoingOps
.counts
[type
]++;
913 switch (outgoingOps
.op
) {
916 /* counting always happens above if not noop */
920 if (p
->header
.type
!= RX_PACKET_TYPE_DATA
)
922 if (rx_GetPacketCksum(p
) == 0) {
923 /* probably, a retransmitted packet */
924 fprintf(stderr
, "Packet cksum already zero\n");
927 rx_SetPacketCksum(p
, 0);
932 if (p
->header
.type
!= RX_PACKET_TYPE_DATA
)
934 cksum
= rx_GetPacketCksum(p
);
936 fprintf(stderr
, "Packet cksum already zero\n");
939 rx_SetPacketCksum(p
, cksum
^ 8);
943 fprintf(stderr
, "Unknown outgoing op %d\n", outgoingOps
.op
);
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
;
956 pthread_mutex_init(&slowCallLock
, NULL
);
957 pthread_cond_init(&slowCallCV
, NULL
);
960 static long slowCallCode
;
962 SlowCall(void * rock
)
964 struct rx_connection
*conn
= rock
;
969 #ifdef AFS_PTHREAD_ENV
970 pthread_mutex_lock(&slowCallLock
);
972 slowCallCode
= RXKST_PROCESSRUNNING
;
973 #ifdef AFS_PTHREAD_ENV
974 pthread_cond_signal(&slowCallCV
);
976 LWP_NoYieldSignal(&slowCallCode
);
978 slowCallCode
= RXKST_Slow(conn
, 1, &ntime
);
980 now
= FT_ApproxTime();
981 if ((ntime
< now
- maxSkew
) || (ntime
> now
+ maxSkew
))
982 slowCallCode
= RXKST_TIMESKEW
;
984 temp_rc
= slowCallCode
;
985 #ifdef AFS_PTHREAD_ENV
986 pthread_cond_signal(&slowCallCV
);
987 pthread_mutex_unlock(&slowCallLock
);
989 LWP_NoYieldSignal(&slowCallCode
);
991 return (void *)(intptr_t)temp_rc
;
994 #endif /* rx_GetPacketCksum */
997 RunHijackTest(struct clientParms
*parms
, long host
,
998 struct rx_securityClass
*sc
, long si
)
1001 #ifndef rx_GetPacketCksum
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");
1011 struct rx_connection
*conn
= 0;
1012 struct rx_connection
*otherConn
= 0;
1013 #ifdef AFS_PTHREAD_ENV
1018 int nResp
; /* otherConn responses seen */
1021 #ifdef AFS_PTHREAD_ENV
1022 pthread_once(&slowCallOnce
, SlowCallInit
);
1024 rx_justReceived
= HandleIncoming
;
1025 rx_almostSent
= HandleOutgoing
;
1027 incomingOps
.op
= IO_NOOP
;
1028 outgoingOps
.op
= OO_NOOP
;
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; }
1041 /* First try switching from no packet cksum to sending packet cksum between
1042 * calls, and see if server complains. */
1044 outgoingOps
.op
= OO_ZEROCKSUM
;
1045 code
= FastCall(conn
);
1047 afs_com_err(whoami
, code
, "doing FastCall with ZEROCKSUM");
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
);
1055 afs_com_err(whoami
, code
, "doing FastCall with non-ZEROCKSUM");
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
);
1062 code
= RXKST_NOBADCKSUM
;
1063 if (code
!= RXKADSEALEDINCON
) {
1064 afs_com_err(whoami
, code
, "doing FastCall with ZEROCKSUM");
1066 } else if (!conn
->error
) {
1067 code
= RXKST_NOCONNERROR
;
1068 afs_com_err(whoami
, code
, "doing FastCall with ZEROCKSUM");
1075 /* Now try modifying packet cksum to see if server complains. */
1077 outgoingOps
.op
= OO_MUNGCKSUM
;
1078 code
= FastCall(conn
);
1080 code
= RXKST_NOBADCKSUM
;
1081 if (code
!= RXKADSEALEDINCON
) {
1082 afs_com_err(whoami
, code
, "doing FastCall with ZEROCKSUM");
1084 } else if (!conn
->error
) {
1085 code
= RXKST_NOCONNERROR
;
1086 afs_com_err(whoami
, code
, "doing FastCall with ZEROCKSUM");
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. */
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)
1114 HIJACK_CONN(otherConn
)
1115 RedirectChallenge(conn
, otherConn
);
1117 code
= FastCall(conn
);
1120 assert(incomingOps
.op
== IO_COUNT
); /* redirect code was triggered */
1121 if (outgoingOps
.counts
[RX_PACKET_TYPE_RESPONSE
] > 0) {
1123 code
= RXKST_CHALLENGEORACLE
;
1124 afs_com_err(whoami
, code
, "misdirecting challenge");
1127 code
= FastCall(otherConn
); /* generate some activity here */
1130 nResp
= outgoingOps
.counts
[RX_PACKET_TYPE_RESPONSE
];
1132 code
= FastCall(conn
);
1135 if (outgoingOps
.counts
[RX_PACKET_TYPE_RESPONSE
] > nResp
)
1139 RedirectChallenge(conn
, otherConn
);
1140 /* otherConn was authenticated during part one */
1141 code
= FastCall(conn
);
1144 assert(incomingOps
.op
== IO_COUNT
); /* redirect code was triggered */
1145 if (outgoingOps
.counts
[RX_PACKET_TYPE_RESPONSE
] != 0)
1149 RedirectChallenge(conn
, otherConn
);
1150 /* otherConn is still authenticated */
1151 slowCallCode
= RXKST_PROCESSCREATED
;
1152 #ifdef AFS_PTHREAD_ENV
1154 pthread_attr_t tattr
;
1156 code
= pthread_attr_init(&tattr
);
1158 afs_com_err(whoami
, code
,
1159 "can't pthread_attr_init slow call process");
1163 code
= pthread_attr_setdetachstate(&tattr
, PTHREAD_CREATE_DETACHED
);
1165 afs_com_err(whoami
, code
,
1166 "can't pthread_attr_setdetachstate slow call process");
1170 code
= pthread_create(&pid
, &tattr
, SlowCall
, (void *)otherConn
);
1174 LWP_CreateProcess(SlowCall
, 16000, LWP_NORMAL_PRIORITY
,
1175 (opaque
) otherConn
, "Slow Call Process", &pid
);
1178 afs_com_err(whoami
, code
, "can't create slow call process");
1181 #ifdef AFS_PTHREAD_ENV
1182 pthread_mutex_lock(&slowCallLock
);
1183 while (slowCallCode
== RXKST_PROCESSCREATED
)
1184 pthread_cond_wait(&slowCallCV
, &slowCallLock
);
1186 while (slowCallCode
== RXKST_PROCESSCREATED
)
1187 LWP_WaitProcess(&slowCallCode
); /* wait for process start */
1189 if (slowCallCode
!= RXKST_PROCESSRUNNING
) {
1190 tmp_rc
= slowCallCode
;
1191 #ifdef AFS_PTHREAD_ENV
1192 pthread_mutex_unlock(&slowCallLock
);
1194 return tmp_rc
; /* make sure didn't fail immediately */
1196 assert(incomingOps
.op
== IO_REDIRECTCHALLENGE
);
1197 code
= FastCall(conn
);
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
);
1206 while (slowCallCode
== RXKST_PROCESSRUNNING
)
1207 LWP_WaitProcess(&slowCallCode
); /* wait for process finish */
1209 if (outgoingOps
.counts
[RX_PACKET_TYPE_RESPONSE
] != 1)
1212 rx_justReceived
= 0;
1214 rx_DestroyConnection(otherConn
);
1215 rx_DestroyConnection(conn
);
1218 #endif /* rx_GetPacketCksum */
1223 rxkst_StartClient(struct clientParms
*parms
)
1228 struct rx_securityClass
*sc
;
1230 whoami
= parms
->whoami
; /* set this global variable */
1232 host
= GetServer(parms
->server
);
1234 if (parms
->authentication
>= 0) {
1236 char ticket
[MAXKTCTICKETLEN
];
1238 struct ktc_encryptionKey Ksession
;
1240 if (parms
->useTokens
)
1242 GetToken(&kvno
, &Ksession
, &ticketLen
, ticket
, parms
->cell
);
1245 GetTicket(&kvno
, &Ksession
, &ticketLen
, ticket
, parms
->cell
);
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
);
1254 scIndex
= RX_SECIDX_KAD
;
1256 /* unauthenticated connection */
1257 sc
= rxnull_NewClientSecurityObject();
1259 scIndex
= RX_SECIDX_NULL
;
1263 if (!code
&& parms
->callTest
) {
1264 code
= RunCallTest(parms
, host
, sc
, scIndex
);
1266 if (!code
&& parms
->hijackTest
) {
1267 code
= RunHijackTest(parms
, host
, sc
, scIndex
);
1270 && (parms
->printTiming
|| parms
->fastCalls
|| parms
->slowCalls
1271 || parms
->copiousCalls
)) {
1272 struct rx_connection
*conn
;
1274 rx_NewConnection(host
, htons(RXKST_SERVICEPORT
), RXKST_SERVICEID
,
1277 code
= RepeatLoadTest(parms
, conn
);
1278 rx_DestroyConnection(conn
);
1280 code
= RXKST_NEWCONNFAILED
;
1282 if (!code
&& parms
->stopServer
) {
1283 struct rx_connection
*conn
;
1285 rx_NewConnection(host
, htons(RXKST_SERVICEPORT
), RXKST_SERVICEID
,
1288 code
= RXKST_Kill(conn
);
1290 afs_com_err(whoami
, code
, "trying to stop server");
1292 rx_DestroyConnection(conn
);
1294 code
= RXKST_NEWCONNFAILED
;
1297 if (parms
->printStats
) {
1298 rx_PrintStats(stdout
);
1300 /* use rxdebug style iteration here */
1301 rx_PrintPeerStats(stdout
, rx_PeerOf(conn
));
1308 afs_com_err(parms
->whoami
, code
, "test fails");
1311 printf("Test Okay\n");