1 /* RADIUS authentication load test */
15 #include <netinet/in.h>
20 #include <sys/select.h>
65 #define USAGE "Usage: %s [-i input] [-n instances] [-f fake] [-b bad] " \
66 "[-l limit] server port secret\n"
68 #define MAX_ATTEMPTS 5
70 void *xmalloc(size_t size
)
72 void *p
= malloc(size
);
75 fprintf(stderr
, "out of memory allocating %d bytes\n", size
);
82 char *xstrdup(char *s
)
85 char *p
= xmalloc(l
+ 1);
89 void *xmmap(size_t size
)
91 void *p
= mmap(NULL
, size
, PROT_READ
|PROT_WRITE
, MAP_SHARED
|MAP_ANONYMOUS
, 0, 0);
95 fprintf(stderr
, "out of memory allocating %d shared bytes\n", size
);
102 void logmsg(char *fmt
, ...)
108 static char time_s
[] = "YYYY-MM-DD HH:MM:SS ";
109 time_t now
= time(NULL
);
111 strftime(time_s
, sizeof(time_s
), "%Y-%m-%d %T ", localtime(&now
));
112 fputs(time_s
, stdout
);
122 new = strchr(fmt
, '\n') != NULL
;
125 void catch(int sig
__attribute__ ((unused
)) ) {}
127 void child(struct user_list
*users
, int count
, int rshift
,
128 struct stats
*stats
, in_addr_t addr
, int port
, int limit
)
129 __attribute__ ((noreturn
));
133 int main(int argc
, char *argv
[])
142 while ((o
= getopt(argc
, argv
, "i:n:f:b:l:")) != -1)
146 case 'i': /* input file */
150 case 'n': /* parallel instances */
151 instances
= atoi(optarg
);
152 if (instances
< 1 || instances
> 32)
154 fprintf(stderr
, "invalid instances value: `%s' (1-32)\n", optarg
);
159 case 'f': /* percentage of additional fake users to add */
161 if (fake
< 1 || fake
> 100)
163 fprintf(stderr
, "invalid fake value: `%s' (1-100)\n", optarg
);
168 case 'b': /* percentage of users to use incorrect passwords for */
170 if (bad
< 1 || bad
> 100)
172 fprintf(stderr
, "invalid bad value: `%s' (1-100)\n", optarg
);
177 case 'l': /* limit number of messages per 1/10 sec */
178 limit
= atoi(optarg
);
181 fprintf(stderr
, "invalid limit value: `%s'\n", optarg
);
187 fprintf(stderr
, USAGE
, argv
[0]);
192 if (argc
- optind
!= 3)
194 fprintf(stderr
, USAGE
, argv
[0]);
198 char *server
= argv
[optind
++];
199 char *port_s
= argv
[optind
++];
200 char *secret
= argv
[optind
];
202 int port
= atoi(port_s
);
205 fprintf(stderr
, "invalid port: `%s'\n", port_s
);
209 in_addr_t server_addr
;
212 if (!(h
= gethostbyname(server
)) || h
->h_addrtype
!= AF_INET
)
214 fprintf(stderr
, "invalid server `%s' (%s)\n", server
,
215 h
? "no address" : hstrerror(h_errno
));
220 memcpy(&server_addr
, h
->h_addr
, sizeof(server_addr
));
223 time(&basetime
); /* start clock */
226 if (input
&& !(in
= fopen(input
, "r")))
228 fprintf(stderr
, "can't open input file `%s' (%s)\n", input
,
234 logmsg("Loading users from %s: ", input
? input
: "stdin");
236 struct user
*users
= 0;
242 while (fgets(buf
, sizeof(buf
), in
))
246 /* format: username \t password \n */
247 char *p
= strchr(buf
, '\t');
250 fprintf(stderr
, "invalid input line %d (no TAB)\n", count
);
257 users
= xmalloc(sizeof(struct user
));
262 u
->next
= xmalloc(sizeof(struct user
));
266 u
->user
= xstrdup(buf
);
270 char *q
= strchr(p
, '\n');
276 fprintf(stderr
, "invalid input line %d (no password)\n", count
);
280 u
->pass
= xstrdup(p
);
288 logmsg("%d\n", count
);
293 char *fake_pw
= "__fake__";
296 /* add f fake users to make a total of which fake% are bogus */
297 int f
= ((count
* fake
) / (100.0 - fake
) + 0.5);
298 char fake_user
[] = "__fake_99999999";
300 logmsg("Generating %d%% extra fake users: ", fake
);
301 for (int i
= 0; i
< f
; i
++, count
++)
303 snprintf(fake_user
, sizeof(fake_user
), "__fake_%d", i
);
304 u
->next
= xmalloc(sizeof(struct user
));
306 u
->user
= xstrdup(fake_user
);
317 int b
= (count
* bad
) / 100.0 + 0.5;
319 logmsg("Setting %d%% bad passwords: ", bad
);
322 for (int i
= 0; i
< b
; i
++, u
= u
->next
)
324 if (u
->pass
!= fake_pw
)
334 struct user
**unsorted
= xmalloc(sizeof(struct user
) * count
);
337 for (int i
= 0; i
< count
; i
++, u
= u
->next
)
340 struct user_list
*random
= xmmap(sizeof(struct user_list
) * count
);
341 memset(random
, 0, sizeof(struct user_list
) * count
);
343 logmsg("Randomising users: ");
345 srand(time(NULL
) ^ getpid());
347 for (int i
= 0; i
< count
; )
349 int j
= 1.0 * count
* rand() / RAND_MAX
;
350 if (unsorted
[j
]->flags
& F_USED
)
353 random
[i
++].entry
= unsorted
[j
];
354 unsorted
[j
]->flags
|= F_USED
;
358 logmsg("Building RADIUS queries: ");
363 for (u
= users
; u
; u
= u
->next
)
365 int pw_len
= strlen(u
->pass
);
366 int len
= 4 /* code, identifier, length */
367 + 16 /* authenticator */
368 + 2 + strlen(u
->user
) /* user */
369 + 2 + ((pw_len
/ 16) + ((pw_len
% 16) ? 1 : 0)) * 16;
370 /* encoded password */
372 char *p
= xmalloc(len
);
374 u
->request_len
= len
;
376 *p
++ = AccessRequest
;
377 *p
++ = 0; /* identifier set in child */
378 *(uint16_t *) p
= htons(len
);
382 for (int j
= 0; j
< 16; j
++)
385 *p
= 1; /* user name */
386 p
[1] = strlen(u
->user
) + 2;
387 strcpy(p
+ 2, u
->user
);
390 strcpy(pass
, u
->pass
);
392 pass
[pw_len
++] = 0; /* pad */
394 for (int j
= 0; j
< pw_len
; j
+= 16)
398 MD5_Update(&ctx
, secret
, strlen(secret
));
400 MD5_Update(&ctx
, pass
+ j
- 16, 16);
403 MD5_Update(&ctx
, u
->request
+ 4, 16);
406 MD5_Final(digest
, &ctx
);
408 for (int k
= 0; k
< 16; k
++)
409 pass
[j
+ k
] ^= digest
[k
];
412 *p
= 2; /* password */
414 memcpy(p
+ 2, pass
, pw_len
);
421 signal(SIGUSR1
, catch);
423 struct stats
*stats
= xmmap(sizeof(struct stats
) * instances
);
424 memset(stats
, 0, sizeof(struct stats
) * instances
);
426 logmsg("Spawning %d processes: ", instances
);
428 int per_child
= count
/ instances
;
430 for (u32 tmp
= per_child
; tmp
& 0xff00; tmp
>>= 1)
433 for (int i
= 0, offset
= 0; i
< instances
; i
++)
435 int slack
= i
? 0 : count
% instances
;
437 stats
[i
].total
= per_child
+ slack
;
439 child(random
+ offset
, per_child
+ slack
, rshift
, stats
+ i
,
440 server_addr
, port
, limit
/ instances
);
442 offset
+= per_child
+ slack
;
447 /* wait for children to setup */
451 for (int i
= 0; i
< instances
; i
++)
452 ready
+= stats
[i
].ready
;
455 } while (ready
< instances
);
460 logmsg("Processing...\n");
463 for (int i
= 0; i
< instances
; i
++)
464 logmsg("[%5d %5s %5s]", stats
[i
].total
, "", "");
467 logmsg(" out/in/err: ");
471 for (int i
= 0; i
< instances
; i
++)
472 logmsg("[%5d %5d %5d]", stats
[i
].out
, stats
[i
].in
,
477 if (waitpid(-1, NULL
, WNOHANG
) > 0)
480 if (done
< instances
)
485 } while (done
< instances
);
487 int a_hist
[MAX_ATTEMPTS
+ 1];
488 memset(&a_hist
, 0, sizeof(a_hist
));
493 memset(&r_hist
, 0, sizeof(r_hist
));
494 int hsz
= sizeof(r_hist
) / sizeof(*r_hist
);
496 for (int i
= 0; i
< count
; i
++)
498 if ((random
[i
].response
!= AccessAccept
&&
499 random
[i
].response
!= AccessReject
) ||
500 (random
[i
].attempts
< 1 ||
501 random
[i
].attempts
> MAX_ATTEMPTS
))
503 a_hist
[MAX_ATTEMPTS
]++;
507 a_hist
[random
[i
].attempts
- 1]++;
509 u32 interval
= random
[i
].end
- random
[i
].begin
;
511 if (!i
|| interval
< min
)
517 /* histogram in 1/10s intervals */
518 int t
= interval
/ 10 + 0.5;
525 logmsg("Send attempts:\n");
526 for (int i
= 0; i
< MAX_ATTEMPTS
; i
++)
527 logmsg(" %6d: %d\n", i
+ 1, a_hist
[i
]);
529 logmsg(" failed: %d\n", a_hist
[MAX_ATTEMPTS
]);
531 logmsg("Response time in seconds (min %.2f, max %.2f)\n",
532 min
/ 100.0, max
/ 100.0);
534 for (int i
= 0; i
< hsz
; i
++)
537 logmsg(" %3.1f:", i
/ 10.0);
541 logmsg(" %6d\n", r_hist
[i
]);
547 /* time in sec/100 since program commenced */
552 return (t
.tv_sec
- basetime
) * 100 + t
.tv_usec
/ 10000 + 1;
555 void child(struct user_list
*users
, int count
, int rshift
,
556 struct stats
*stats
, in_addr_t addr
, int port
, int limit
)
558 int sockets
= 1 << rshift
;
559 unsigned rmask
= sockets
- 1;
561 int *sock
= xmalloc(sizeof(int) * sockets
);
568 for (int s
= 0; s
< sockets
; s
++)
570 if ((sock
[s
] = socket(AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
)) < 0)
572 fprintf(stderr
, "can't create a UDP socket (%s)\n",
578 int flags
= fcntl(sock
[s
], F_GETFL
, 0);
579 fcntl(sock
[s
], F_SETFL
, flags
| O_NONBLOCK
);
581 struct sockaddr_in svr
;
582 memset(&svr
, 0, sizeof(svr
));
583 svr
.sin_family
= AF_INET
;
584 svr
.sin_port
= htons(port
);
585 svr
.sin_addr
.s_addr
= addr
;
587 connect(sock
[s
], (struct sockaddr
*) &svr
, sizeof(svr
));
589 FD_SET(sock
[s
], &r_in
);
590 if (sock
[s
] + 1 > nfd
)
594 for (int i
= 0; i
< count
; i
++)
596 *((unsigned char *) users
[i
].entry
->request
+ 1) = i
>> rshift
;
601 u32 out_timer
= now();
604 while ((stats
->in
+ stats
->err
) < count
)
606 u32 time_now
= now();
608 while (out_timer
+ 10 < time_now
)
615 for (int pass
= 1; pass
<= 2; pass
++)
617 for (int i
= 0; i
< count
&& out_count
< limit
; i
++)
619 if (users
[i
].response
)
622 if (users
[i
].attempts
)
624 if (users
[i
].retry
> time_now
)
629 /* retries only on the first pass */
633 struct user
*e
= users
[i
].entry
;
634 if (write(sock
[i
& rmask
], e
->request
, e
->request_len
)
641 if (!users
[i
].attempts
)
643 users
[i
].begin
= time_now
;
647 if (++users
[i
].attempts
> MAX_ATTEMPTS
)
649 users
[i
].response
= AccessFail
;
654 users
[i
].retry
= time_now
+ 200 + 100 * (1 << users
[i
].attempts
);
658 struct timeval tv
= { 0, 100000 };
661 memcpy(&r
, &r_in
, sizeof(r
));
663 if (select(nfd
, &r
, NULL
, NULL
, &tv
) < 1)
668 for (int s
= 0; s
< sockets
; s
++)
670 if (!FD_ISSET(sock
[s
], &r
))
675 while ((sz
= read(sock
[s
], buf
, sizeof(buf
))) > 0)
679 fprintf(stderr
, "short packet returned\n");
683 if (buf
[0] != AccessAccept
&& buf
[0] != AccessReject
)
685 fprintf(stderr
, "unrecognised response type %d\n",
691 int i
= s
| (((unsigned char) buf
[1]) << rshift
);
692 if (i
< 0 || i
> count
)
694 fprintf(stderr
, "bogus identifier returned %d\n", i
);
698 if (!users
[i
].attempts
)
700 fprintf(stderr
, "unexpected identifier returned %d\n", i
);
704 if (users
[i
].response
)
707 int expect
= (users
[i
].entry
->flags
& (F_FAKE
|F_BAD
))
708 ? AccessReject
: AccessAccept
;
710 if (buf
[0] != expect
)
711 fprintf(stderr
, "unexpected response %d for user %s "
712 "(expected %d)\n", (int) buf
[0], users
[i
].entry
->user
,
715 users
[i
].response
= buf
[0];
716 users
[i
].end
= now();