1 //==========================================================================
3 // tests/nc6_test_master.c
5 // Network characterizations test (master portion) IPv4+IPv6 aware
7 //==========================================================================
8 //####BSDCOPYRIGHTBEGIN####
10 // -------------------------------------------
12 // Portions of this software may have been derived from OpenBSD or other sources,
13 // and are covered by the appropriate copyright disclaimers included herein.
15 // -------------------------------------------
17 //####BSDCOPYRIGHTEND####
18 //==========================================================================
19 //#####DESCRIPTIONBEGIN####
22 // Contributors: gthomas
28 //####DESCRIPTIONEND####
30 //==========================================================================
32 // Network characterization test code - master portion
34 #include "nc_test_framework.h"
37 #ifndef CYGPKG_LIBC_STDIO
38 #define perror(s) diag_printf(#s ": %s\n", strerror(errno))
40 #define STACK_SIZE (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x1000)
41 static char stack[STACK_SIZE];
42 static cyg_thread thread_data;
43 static cyg_handle_t thread_handle;
51 #define MAX_BUF 32*1024
52 static unsigned char in_buf[MAX_BUF], out_buf[MAX_BUF];
54 static int test_seq = 1;
55 static long long idle_count;
56 static long idle_ticks;
57 #define IDLE_TEST_TIME 10
63 #define LENGTH(x) (sizeof(x)/sizeof(x[0]))
72 test_printf("... Done\n");
81 cyg_thread_delay(ticks);
89 usleep(ticks * 10000);
101 #ifndef CYGPKG_SNMPLIB
103 gettimeofday(struct timeval *tv, struct timezone *tz)
105 cyg_tick_count_t cur_time;
106 cur_time = cyg_current_time();
107 tv->tv_sec = cur_time / 100;
108 tv->tv_usec = (cur_time % 100) * 10000;
112 gettimeofday(struct timeval *tv, struct timezone *tz);
116 show_results(const char *msg, struct timeval *start,
117 struct timeval *end, int nbufs, int buflen,
118 int lost, int seq_errors)
120 struct timeval tot_time;
122 double real_time, thru;
123 long tot_bytes = nbufs * buflen;
125 timersub(end, start, &tot_time);
126 test_printf("%s - %d bufs of %d bytes in %d.%02d seconds",
128 tot_time.tv_sec, tot_time.tv_usec / 10000);
130 real_time = tot_time.tv_sec + ((tot_time.tv_usec / 10000) * .01);
131 // Compute bytes / second (rounded up)
132 thru = tot_bytes / real_time;
133 // Convert to Mb / second
134 test_printf(" - %.2f KB/S", thru / 1024.0);
135 test_printf(" - %.4f Mbit/S (M = 10^6)", thru * 8.0 / 1000000.0);
138 test_printf(", %d lost", lost);
141 test_printf(", %d out of sequence", seq_errors);
153 sa_len(struct sockaddr *sa)
155 switch (sa->sa_family) {
157 return sizeof(struct sockaddr_in);
159 return sizeof(struct sockaddr_in6);
161 printf("Unknown socket type: %d\n", sa->sa_family);
162 return sizeof(struct sockaddr_storage);
167 nc_message(int s, struct nc_request *req,
168 struct nc_reply *reply, struct sockaddr *slave)
171 struct timeval timeout;
173 req->seq = htonl(test_seq);
174 if (sendto(s, req, sizeof(*req), 0, slave, sa_len(slave)) < 0) {
180 timeout.tv_sec = NC_REPLY_TIMEOUT;
182 if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
183 test_printf("No response to command\n");
186 if (recvfrom(s, reply, sizeof(*reply), 0, 0, 0) < 0) {
190 if (reply->seq != req->seq) {
191 test_printf("Response out of order - sent: %d, recvd: %d\n",
192 ntohl(req->seq), ntohl(reply->seq));
199 show_test_results(struct nc_test_results *results)
201 if ((ntohl(results->key1) == NC_TEST_RESULT_KEY1) &&
202 (ntohl(results->key2) == NC_TEST_RESULT_KEY2) &&
203 (ntohl(results->seq) == test_seq)) {
204 test_printf(" slave sent %d, recvd %d\n",
205 ntohl(results->nsent), ntohl(results->nrecvd));
207 test_printf(" ... invalid results - keys: %x/%x, seq: %d/%d\n",
208 ntohl(results->key1), ntohl(results->key2),
209 ntohl(results->seq), test_seq);
214 do_udp_test(int s1, int type, struct sockaddr *slave,
215 int nbufs, int buflen, int pause_time, int pause_threshold)
217 int i, s, td_len, seq, seq_errors, total_packets;
218 struct sockaddr_storage test_chan_master, test_chan_slave;
219 struct timeval start_time, end_time;
220 struct nc_request req;
221 struct nc_reply reply;
222 struct nc_test_results results;
223 struct nc_test_data *tdp;
225 struct timeval timeout;
226 int lost_packets = 0;
227 int need_send, need_recv;
228 const char *type_name;
231 need_recv = true; need_send = true; type_name = "UDP echo";
233 case NC_REQUEST_UDP_RECV:
236 type_name = "UDP recv";
238 case NC_REQUEST_UDP_SEND:
241 type_name = "UDP send";
243 case NC_REQUEST_UDP_ECHO:
248 req.type = htonl(type);
249 req.nbufs = htonl(nbufs);
250 req.buflen = htonl(buflen);
251 req.slave_port = htonl(NC_TESTING_SLAVE_PORT);
252 req.master_port = htonl(NC_TESTING_MASTER_PORT);
253 nc_message(s1, &req, &reply, slave);
254 if (reply.response != ntohl(NC_REPLY_ACK)) {
255 test_printf("Slave denied %s [%d,%d] test\n", type_name, nbufs, buflen);
259 s = socket(slave->sa_family, SOCK_DGRAM, 0);
261 pexit("datagram socket");
264 memset(&test_chan_master, 0, sizeof(test_chan_master));
265 ((struct sockaddr *)&test_chan_master)->sa_family = slave->sa_family;
266 memcpy(&test_chan_slave, slave, sa_len(slave));
268 ((struct sockaddr *)&test_chan_master)->sa_len = slave->sa_len;
270 switch (slave->sa_family) {
272 ((struct sockaddr_in *)&test_chan_master)->sin_addr.s_addr = htonl(INADDR_ANY);
273 ((struct sockaddr_in *)&test_chan_master)->sin_port = htons(ntohl(req.master_port));
274 ((struct sockaddr_in *)&test_chan_slave)->sin_port = htons(ntohl(req.slave_port));
277 ((struct sockaddr_in6 *)&test_chan_master)->sin6_addr = in6addr_any;
278 ((struct sockaddr_in6 *)&test_chan_master)->sin6_port = htons(ntohl(req.master_port));
279 ((struct sockaddr_in6 *)&test_chan_slave)->sin6_port = htons(ntohl(req.slave_port));
282 pexit("strange TCP sockaddr");
285 if (bind(s, (struct sockaddr *)&test_chan_master,
286 sa_len((struct sockaddr *)&test_chan_master)) < 0) {
287 perror("UDP bind <do_udp_test>");
291 test_printf("Start %s [%d,%d]", type_name, nbufs, buflen);
293 test_printf(" - %dms delay after %d packet%s\n", pause_time*10,
294 pause_threshold, pause_threshold > 1 ? "s" : "");
296 test_printf(" - no delays\n");
299 gettimeofday(&start_time, 0);
300 seq = 0; seq_errors = 0; total_packets = 0;
301 for (i = 0; i < nbufs; i++) {
302 td_len = buflen + sizeof(struct nc_test_data);
304 tdp = (struct nc_test_data *)out_buf;
305 tdp->key1 = htonl(NC_TEST_DATA_KEY1);
306 tdp->key2 = htonl(NC_TEST_DATA_KEY2);
308 tdp->len = htonl(td_len);
309 if (sendto(s, tdp, td_len, 0,
310 (struct sockaddr *)&test_chan_slave,
311 sa_len((struct sockaddr *)&test_chan_slave)) < 0) {
321 timeout.tv_sec = NC_TEST_TIMEOUT;
323 if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
324 test_printf("Slave timed out after %d buffers\n", i);
327 tdp = (struct nc_test_data *)in_buf;
328 if (recvfrom(s, tdp, td_len, 0, 0, 0) < 0) {
333 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
334 (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
335 if (ntohl(tdp->seq) != seq) {
336 test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
337 ntohl(tdp->seq), seq);
340 // Reset sequence to what the slave wants
341 seq = ntohl(tdp->seq);
345 test_printf("Bad data packet - key: %x/%x, seq: %d\n",
346 ntohl(tdp->key1), ntohl(tdp->key2),
355 if (pause_time && (++pkt_ctr == pause_threshold)) {
357 test_delay(pause_time);
361 gettimeofday(&end_time, 0);
362 show_results(type_name, &start_time, &end_time, total_packets, buflen,
363 lost_packets, seq_errors);
364 // Fetch results record
367 timeout.tv_sec = NC_RESULTS_TIMEOUT;
369 if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
370 test_printf("No results record sent\n");
372 if (recvfrom(s, &results, sizeof(results), 0, 0, 0) < 0) {
375 show_test_results(&results);
381 // Read data from a stream, accounting for the fact that packet 'boundaries'
382 // are not preserved. This can also timeout (which would probably wreck the
387 do_read(int fd, void *buf, int buflen)
389 char *p = (char *)buf;
393 res = read(fd, p, len);
404 return (buflen - len);
408 do_tcp_test(int s1, int type, struct sockaddr *slave,
409 int nbufs, int buflen, int pause_time, int pause_threshold)
411 int i, s, td_len, tot_len, wlen, len, seq, seq_errors, total_packets, res;
412 struct sockaddr_storage test_chan_slave;
413 struct timeval start_time, end_time;
414 struct nc_request req;
415 struct nc_reply reply;
416 struct nc_test_results results;
417 struct nc_test_data *tdp;
418 int lost_packets = 0;
419 int conn_failures = 0;
420 int need_send, need_recv;
421 const char *type_name;
425 need_recv = true; need_send = true; type_name = "TCP echo";
427 case NC_REQUEST_TCP_RECV:
430 type_name = "TCP recv";
432 case NC_REQUEST_TCP_SEND:
435 type_name = "TCP send";
437 case NC_REQUEST_TCP_ECHO:
442 req.type = htonl(type);
443 req.nbufs = htonl(nbufs);
444 req.buflen = htonl(buflen);
445 req.slave_port = htonl(NC_TESTING_SLAVE_PORT);
446 req.master_port = htonl(NC_TESTING_MASTER_PORT);
447 nc_message(s1, &req, &reply, slave);
448 if (reply.response != ntohl(NC_REPLY_ACK)) {
449 test_printf("Slave denied %s [%d,%d] test\n", type_name, nbufs, buflen);
453 s = socket(slave->sa_family, SOCK_STREAM, 0);
455 pexit("datagram socket");
458 test_printf("Start %s [%d,%d]", type_name, nbufs, buflen);
460 test_printf(" - %dms delay after %d packet%s\n", pause_time*10,
461 pause_threshold, pause_threshold > 1 ? "s" : "");
463 test_printf(" - no delays\n");
467 memcpy(&test_chan_slave, slave, sa_len(slave));
468 switch (slave->sa_family) {
470 ((struct sockaddr_in *)&test_chan_slave)->sin_port = htons(ntohl(req.slave_port));
473 ((struct sockaddr_in6 *)&test_chan_slave)->sin6_port = htons(ntohl(req.slave_port));
476 pexit("strange TCP sockaddr");
478 while (connect(s, (struct sockaddr *)&test_chan_slave, sa_len(slave)) < 0) {
479 perror("Can't connect to slave");
480 if (++conn_failures > MAX_ERRORS) {
481 test_printf("Too many connection failures - giving up\n");
484 if (errno == ECONNREFUSED) {
485 // Give the slave a little time
486 test_delay(100); // 1 second
492 gettimeofday(&start_time, 0);
493 seq = 0; seq_errors = 0; total_packets = 0;
494 for (i = 0; i < nbufs; i++) {
495 td_len = buflen + sizeof(struct nc_test_data);
497 tdp = (struct nc_test_data *)out_buf;
498 tdp->key1 = htonl(NC_TEST_DATA_KEY1);
499 tdp->key2 = htonl(NC_TEST_DATA_KEY2);
501 tdp->len = htonl(td_len);
503 dp = (unsigned char *)tdp;
504 while (tot_len < td_len) {
505 len = td_len - tot_len;
506 if ((wlen = write(s, dp, len)) != len) {
508 test_printf("Slave connection broken\n");
513 test_printf("block: %d, short write - only %d of %d\n",
514 total_packets, wlen, len);
523 tdp = (struct nc_test_data *)in_buf;
524 res = do_read(s, tdp, td_len);
528 test_printf("Slave connection broken\n");
532 test_printf("Slave timed out after %d buffers [read %d bytes]\n", i, res);
535 if ((ntohl(tdp->key1) == NC_TEST_DATA_KEY1) &&
536 (ntohl(tdp->key2) == NC_TEST_DATA_KEY2)) {
537 if (ntohl(tdp->seq) != seq) {
538 test_printf("Packets out of sequence - recvd: %d, expected: %d\n",
539 ntohl(tdp->seq), seq);
542 // Reset sequence to what the slave wants
543 seq = ntohl(tdp->seq);
547 test_printf("Bad data packet - key: %x/%x, seq: %d\n",
548 ntohl(tdp->key1), ntohl(tdp->key2),
557 if (pause_time && (++pkt_ctr == pause_threshold)) {
559 test_delay(pause_time);
563 gettimeofday(&end_time, 0);
564 show_results(type_name, &start_time, &end_time, total_packets, buflen,
565 lost_packets, seq_errors);
566 // Fetch results record
567 if (do_read(s, &results, sizeof(results)) != sizeof(results)) {
568 test_printf("No results record sent\n");
570 show_test_results(&results);
576 do_set_load(int s, struct sockaddr *slave, int load_level)
578 struct nc_request req;
579 struct nc_reply reply;
580 req.type = htonl(NC_REQUEST_SET_LOAD);
581 req.nbufs = htonl(load_level);
582 nc_message(s, &req, &reply, slave);
583 return (reply.response == ntohl(NC_REPLY_ACK));
587 do_start_idle(int s, struct sockaddr *slave)
589 struct nc_request req;
590 struct nc_reply reply;
591 req.type = htonl(NC_REQUEST_START_IDLE);
592 nc_message(s, &req, &reply, slave);
593 return (reply.response == ntohl(NC_REPLY_ACK));
597 do_stop_idle(int s, struct sockaddr *slave, int calibrate)
599 struct nc_request req;
600 struct nc_reply reply;
601 long long res_idle_count;
603 int idle, res_idle_ticks;
604 req.type = htonl(NC_REQUEST_STOP_IDLE);
605 nc_message(s, &req, &reply, slave);
606 if (reply.response == ntohl(NC_REPLY_ACK)) {
607 res_idle_ticks = ntohl(reply.misc.idle_results.elapsed_time);
608 res_idle_count = ((long long)ntohl(reply.misc.idle_results.count[0]) << 32) |
609 ntohl(reply.misc.idle_results.count[1]);
610 test_printf("IDLE - ticks: %d, count: %ld",
611 res_idle_ticks, res_idle_count);
613 idle_count = res_idle_count;
614 idle_ticks = res_idle_ticks;
616 adj_count = res_idle_count / res_idle_ticks;
617 adj_count *= idle_ticks;
618 idle = (int) ((adj_count * 100) / idle_count);
619 test_printf(", %d%% idle", idle);
623 test_printf("Slave failed on IDLE\n");
628 do_disconnect(int s, struct sockaddr *slave)
630 struct nc_request req;
631 struct nc_reply reply;
632 req.type = htonl(NC_REQUEST_DISCONNECT);
633 nc_message(s, &req, &reply, slave);
637 nc_master_test(struct sockaddr *slave, int test_tcp, int test_udp,
638 int test_slave_loads, int test_master_loads)
641 struct sockaddr_storage my_addr;
642 struct pause pause_times[] = {
643 {0,0}, {1,10}, {5,10}, {10,10}, {1,1} };
646 s = socket(slave->sa_family, SOCK_DGRAM, 0);
648 pexit("datagram socket");
651 memset(&my_addr, 0, sizeof(my_addr));
652 ((struct sockaddr *)&my_addr)->sa_family = slave->sa_family;
654 ((struct sockaddr *)&my_addr)->sa_len = slave->sa_len;
656 switch (slave->sa_family) {
658 ((struct sockaddr_in *)&my_addr)->sin_addr.s_addr = htonl(INADDR_ANY);
659 ((struct sockaddr_in *)&my_addr)->sin_port = htons(NC_MASTER_PORT);
662 ((struct sockaddr_in6 *)&my_addr)->sin6_addr = in6addr_any;
663 ((struct sockaddr_in6 *)&my_addr)->sin6_port = htons(NC_MASTER_PORT);
666 pexit("strange sockaddr family");
668 if (bind(s, (struct sockaddr *) &my_addr, sa_len((struct sockaddr *)&my_addr)) < 0) {
669 pexit("UDP bind <main>");
672 test_printf("================== No load, master at 100%% ========================\n");
674 do_udp_test(s, NC_REQUEST_UDP_ECHO, slave, 640, 1024, 0, 0);
675 do_udp_test(s, NC_REQUEST_UDP_SEND, slave, 640, 1024, 0, 0);
676 do_udp_test(s, NC_REQUEST_UDP_RECV, slave, 640, 1024, 0, 0);
679 do_tcp_test(s, NC_REQUEST_TCP_ECHO, slave, 640, 1024, 0, 0);
680 do_tcp_test(s, NC_REQUEST_TCP_SEND, slave, 640, 1024, 0, 0);
681 do_tcp_test(s, NC_REQUEST_TCP_RECV, slave, 640, 1024, 0, 0);
682 do_tcp_test(s, NC_REQUEST_TCP_ECHO, slave, 64, 10240, 0, 0);
685 if (test_slave_loads) {
686 if (do_set_load(s, slave, 0)) {
687 test_printf("\n====================== Various slave compute loads ===================\n");
688 for (i = 0; i < 60; i += 10) {
689 test_printf(">>>>>>>>>>>> slave processing load at %d%%\n", i);
690 do_set_load(s, slave, i);
692 do_udp_test(s, NC_REQUEST_UDP_ECHO, slave, 2048, 1024, 0, 0);
695 do_tcp_test(s, NC_REQUEST_TCP_ECHO, slave, 2048, 1024, 0, 0);
701 if (test_master_loads) {
702 if (do_start_idle(s, slave)) {
703 test_printf("\n====================== Various master loads ===================\n");
704 test_printf("Testing IDLE for %d seconds\n", IDLE_TEST_TIME);
705 test_delay(IDLE_TEST_TIME*100);
706 do_stop_idle(s, slave, true);
707 for (i = 0; i < LENGTH(pause_times); i++) {
709 do_start_idle(s, slave);
710 do_udp_test(s, NC_REQUEST_UDP_ECHO, slave, 2048, 1024,
711 pause_times[i].pause_ticks, pause_times[i].pause_threshold);
712 do_stop_idle(s, slave, false);
715 do_start_idle(s, slave);
716 do_tcp_test(s, NC_REQUEST_TCP_ECHO, slave, 2048, 1024,
717 pause_times[i].pause_ticks, pause_times[i].pause_threshold);
718 do_stop_idle(s, slave, false);
724 // do_disconnect(s, slave);
729 nc_master(struct test_params *p)
731 struct sockaddr_storage slave, my_addr;
732 struct addrinfo *ai, *addrs, hints;
733 char *host = (char *)NULL;
738 int test_slave_loads = true;
739 int test_master_loads = true;
741 for (i = 1; i < p->argc; i++) {
742 if (p->argv[i][0] == '-') {
743 switch (p->argv[i][1]) {
751 test_slave_loads = false;
754 test_master_loads = false;
757 test_printf("... invalid switch '%s'\n", p->argv[i]);
761 if (host != (char *)NULL) {
762 test_printf("... ignoring argument '%s'\n", p->argv[i]);
769 if ((err != 0) || (p->argc < 2) || (host == (char *)NULL)) {
770 test_printf("usage: 'master <host> [-t] [-u] [-s] [-m]'\n");
771 test_printf(" -t - suppress TCP tests\n");
772 test_printf(" -u - suppress UDP tests\n");
773 test_printf(" -s - suppress slave load tests\n");
774 test_printf(" -m - suppress master load tests\n");
777 bzero(&hints, sizeof(hints));
778 hints.ai_family = PF_UNSPEC;
779 hints.ai_socktype = SOCK_DGRAM;
780 hints.ai_flags = AI_PASSIVE;
781 if ((err = getaddrinfo(p->argv[1], _string(NC_SLAVE_PORT), &hints, &addrs)) != EAI_NONE) {
782 test_printf("<ERROR> can't getaddrinfo(): %s\n", gai_strerror(err));
783 pexit("getaddrinfo");
785 // Prepare a socket for each connection type
788 nc_master_test(ai->ai_addr, test_tcp, test_udp, test_slave_loads, test_master_loads);
794 net_test(test_param_t p)
796 test_printf("Start Network Characterization - MASTER\n");
798 init_all_network_interfaces();
800 nc_master((struct test_params *)p);
808 static struct test_params p;
809 // Create a main thread, so we can run the scheduler and have time 'pass'
810 cyg_thread_create(10, // Priority - just a number
812 (cyg_addrword_t)&p,// entry parameter
813 "Network test", // Name
816 &thread_handle, // Handle
817 &thread_data // Thread data structure
819 cyg_thread_resume(thread_handle); // Start it
820 cyg_scheduler_start();
826 main(int argc, char *argv[])
828 struct test_params p;