commit 18b1a53fd09b02cab0117b08ab91e8881a8e0e7d
Author: Jan Tluka <jtluka(a)redhat.com>
Date: Fri Apr 12 13:04:18 2013 +0200
Reimplementing TCPConnect and TCPListen test as a test tool in C
The TCPConnect and TCPListen written in Python had issues that were hard
to debug. The main problem was with the test hangs during tests. Another
issue is that these tests are using threads. Since the mixing of fork and
threading is strongly discouraged in Python I rewrote these tests as test
tools tcp_connect and tcp_listen under tcp_conn directory in C language
using direct system calls for networking routines and using processes for
handling connections.
Example usage:
<command_sequence>
<define>
<alias name="my_range" value="10000-10050" />
</define>
<command machine_id="testmachine2" type="exec" from="tcp_conn"
value="./tcp_listen -p {$my_range} -a {ip(testmachine2,testifc1)} -c"
bg_id="1"/>
<command type="ctl_wait" value="3" />
<command machine_id="testmachine1" type="exec" from="tcp_conn"
value="./tcp_connect -p {$my_range} -a {ip(testmachine2,testifc1)} -c"
bg_id="2"/>
<command machine_id="testmachine1" type="exec" from="tcp_conn"
value="./tcp_listen -p {$my_range} -a {ip(testmachine1,testifc1)} -c"
bg_id="3"/>
<command type="ctl_wait" value="3" />
<command machine_id="testmachine2" type="exec" from="tcp_conn"
value="./tcp_connect -p {$my_range} -a {ip(testmachine1,testifc1)} -c"
bg_id="4"/>
<command type="ctl_wait" value="100" />
<command machine_id="testmachine1" type="intr" value="2" />
<command machine_id="testmachine2" type="intr" value="1" />
<command machine_id="testmachine2" type="intr" value="4" />
<command machine_id="testmachine1" type="intr" value="3" />
</command_sequence>
Options are common both for server and client part:
-p port_range ... specifies the range of ports to use under test
-c ... use continuous mode otherwise just do a single
connection and end
-d ... turn on debug output, use with caution since this
produces a lot of data
Signed-off-by: Jan Tluka <jtluka(a)redhat.com>
test_modules/TestTCPConnect.py | 205 ---------------------------------
test_modules/TestTCPListen.py | 198 --------------------------------
test_tools/tcp_conn/Makefile | 12 ++
test_tools/tcp_conn/lnst-setup.sh | 3 +
test_tools/tcp_conn/tcp_connect.c | 202 ++++++++++++++++++++++++++++++++
test_tools/tcp_conn/tcp_listen.c | 229 +++++++++++++++++++++++++++++++++++++
6 files changed, 446 insertions(+), 403 deletions(-)
---
diff --git a/test_tools/tcp_conn/Makefile b/test_tools/tcp_conn/Makefile
new file mode 100644
index 0000000..2e8f5e6
--- /dev/null
+++ b/test_tools/tcp_conn/Makefile
@@ -0,0 +1,12 @@
+CC=gcc
+
+all: tcp_listen tcp_connect
+
+tcp_listen: tcp_listen.c
+ $(CC) -o tcp_listen tcp_listen.c
+
+tcp_connect: tcp_connect.c
+ $(CC) -o tcp_connect tcp_connect.c
+
+clean:
+ rm -f tcp_connect tcp_listen
diff --git a/test_tools/tcp_conn/lnst-setup.sh b/test_tools/tcp_conn/lnst-setup.sh
new file mode 100755
index 0000000..2aad486
--- /dev/null
+++ b/test_tools/tcp_conn/lnst-setup.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+make
diff --git a/test_tools/tcp_conn/tcp_connect.c b/test_tools/tcp_conn/tcp_connect.c
new file mode 100644
index 0000000..598ac80
--- /dev/null
+++ b/test_tools/tcp_conn/tcp_connect.c
@@ -0,0 +1,202 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#include <errno.h>
+#include <signal.h>
+
+
+int connection_handlers[1024];
+int handlers_count;
+
+int debug_on = 0;
+int cont = 0;
+
+int usage()
+{
+ printf("./tcp_connect -p [port_range] -a [ipaddr]\n");
+ return 0;
+}
+
+#define MSG_MAX 256
+char msg[MSG_MAX];
+
+int debug(char* msg)
+{
+ if (debug_on)
+ {
+ printf("Debug: %s\n", msg);
+ }
+}
+
+void terminate_connections(int p)
+{
+ int cc;
+
+ debug("signalled");
+ for (cc=0; cc < handlers_count; cc++)
+ {
+ snprintf(msg, MSG_MAX, "killing process %i", connection_handlers[cc]);
+ debug(msg);
+ kill(connection_handlers[cc], SIGKILL);
+ }
+}
+
+int handle_connections(char* host, int port)
+{
+ int conn_sock;
+ int remote_sock;
+ struct sockaddr_in my_addr;
+ struct sockaddr remote;
+ int end_loop = 0;
+ char data[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+ char buf[21*10*strlen(data)+1];
+
+ snprintf(msg, MSG_MAX, "Starting connection on %s port %i", host, port);
+ debug(msg);
+
+ my_addr.sin_family = AF_INET;
+ my_addr.sin_port = htons(port);
+ if (inet_aton(host, &(my_addr.sin_addr)) == 0)
+ {
+ printf("failed on inet_aton()\n");
+ return 1;
+ }
+
+ do
+ {
+ conn_sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (conn_sock == -1)
+ {
+ perror("fail on socket()");
+ return 1;
+ }
+
+ if (connect(conn_sock, (struct sockaddr*) &my_addr, sizeof(struct sockaddr_in)) == -1)
+ {
+ perror("fail on connect");
+ return 1;
+ }
+
+ int bursts = 5*(random() % 10) + 1;
+ int b;
+ int sum = 0;
+
+ for (b=0; b < bursts; b++)
+ {
+ int parts = 20*(random() % 10) + 1;
+ int j;
+
+ sum += parts*strlen(data);
+ for (j = 0; j < parts; j++)
+ {
+ strncpy(buf + (j*strlen(data)), data, strlen(data));
+ }
+
+ int wr_rc = write(conn_sock, buf, parts * strlen(data));
+ usleep(100*(random()%100));
+ }
+
+ snprintf(msg, MSG_MAX, "sent %i bytes (bursts: %i)", sum, bursts);
+ debug(msg);
+ snprintf(msg, MSG_MAX, "closing connection on port %i", port);
+ debug(msg);
+
+ close(conn_sock);
+ } while (cont);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int rc;
+ int p;
+ char port_str[256];
+ char str_port_start[128];
+ char str_port_end[128];
+ char host_str[16] = "\0";
+ int start_port;
+ int end_port;
+ int opt;
+ char *delimiter;
+ struct sigaction sa;
+
+ sa.sa_handler = &terminate_connections;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ handlers_count = 0;
+
+ /* collect program args */
+ while ((opt = getopt(argc, argv, "p:a:dc")) != -1) {
+ switch (opt) {
+ case 'p':
+ strncpy(port_str, optarg, 256);
+ port_str[256-1]='\0';
+ delimiter = strchr(port_str, '-');
+ if (delimiter == NULL)
+ {
+ usage();
+ }
+ strncpy(str_port_start, port_str, delimiter - port_str);
+ str_port_start[delimiter - port_str] = '\0';
+ strncpy(str_port_end, delimiter+1, strlen(port_str) - (delimiter+1-port_str));
+ start_port = atoi(str_port_start);
+ end_port = atoi(str_port_end);
+ break;
+ case 'a':
+ strncpy(host_str, optarg, 64);
+ host_str[16-1] = '\0';
+ break;
+ case 'd':
+ debug_on = 1;
+ break;
+ case 'c':
+ cont = 1;
+ break;
+ }
+ }
+
+ if (strlen(host_str) == 0)
+ {
+ usage();
+ return 1;
+ }
+
+ /* spawn process to handle every port specified */
+ for (p = start_port; p < end_port; p++){
+ if ((rc = fork()) > 0)
+ {
+ /* parent, add pid to the list */
+ connection_handlers[handlers_count++] = rc;
+ }
+ else if (rc == 0)
+ {
+ /* child */
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ handle_connections(host_str, p);
+ return 0;
+ }
+ }
+
+ /* gather children */
+ int child_status;
+ int i;
+ for (i=0; i < handlers_count; i++)
+ {
+ wait(&child_status);
+ debug("worker finished");
+ }
+
+ debug("tcp_connect finished");
+
+ return 0;
+}
diff --git a/test_tools/tcp_conn/tcp_listen.c b/test_tools/tcp_conn/tcp_listen.c
new file mode 100644
index 0000000..df07f69
--- /dev/null
+++ b/test_tools/tcp_conn/tcp_listen.c
@@ -0,0 +1,229 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#include <errno.h>
+#include <signal.h>
+
+int listener_count;
+int listeners[1024];
+
+int connection_count;
+int debug_on = 0;
+int cont = 0;
+
+int usage()
+{
+ printf("./tcp_listen -p [port_range] -a [ipaddr]\n");
+ return 0;
+}
+
+int debug(char* msg)
+{
+ if (debug_on)
+ {
+ printf("Debug: %s\n", msg);
+ }
+}
+
+#define MSG_MAX 256
+char msg[MSG_MAX];
+
+void terminate_connections(int p)
+{
+ int l;
+
+ debug("signal received, killing servers");
+ for (l=0; l < listener_count; l++)
+ {
+ snprintf(msg, MSG_MAX, "killing process %i", listeners[l]);
+ debug(msg);
+ kill(listeners[l], SIGKILL);
+ }
+}
+
+void connection_counter(int p)
+{
+ debug("increase counter");
+ connection_count += 1;
+}
+
+int handle_connections(char* host, int port)
+{
+ int listen_sock;
+ struct sockaddr_in my_addr;
+ socklen_t my_addr_size = sizeof(my_addr);
+ int remote_sock;
+ struct sockaddr_in remote;
+ socklen_t remote_size = sizeof(remote);
+ char data[256];
+
+ snprintf(msg, MSG_MAX, "Starting listener on %s port %i", host, port);
+ debug(msg);
+
+ bzero(&my_addr, my_addr_size);
+ my_addr.sin_family = AF_INET;
+ my_addr.sin_port = htons(port);
+ if (inet_aton(host, &(my_addr.sin_addr)) == 0)
+ {
+ printf("failed on inet_aton()\n");
+ return 1;
+ }
+
+ if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
+ {
+ perror("fail on socket creation");
+ return 1;
+ }
+
+ if (bind(listen_sock, (struct sockaddr*) &my_addr, sizeof(struct sockaddr_in)))
+ {
+ perror("fail on bind");
+ return 1;
+ }
+
+ if (listen(listen_sock, 0))
+ {
+ perror("fail on listen");
+ return 1;
+ }
+
+ do
+ {
+ struct in_addr ra = remote.sin_addr;
+ char host_address_str[256];
+ ssize_t read_rc;
+
+ /* handle single connection */
+ bzero(&remote, remote_size);
+ remote_sock = accept(listen_sock, (struct sockaddr*) &remote, &remote_size);
+ if (remote_sock == -1)
+ {
+ perror("failure on accept");
+ close(listen_sock);
+ return 1;
+ }
+
+ inet_ntop(AF_INET, &ra, host_address_str, 255);
+ snprintf(msg, MSG_MAX, "accepted connection from host %s port %i", host_address_str, port);
+ debug(msg);
+
+ int sum = 0;
+ while (read_rc = read(remote_sock, &data, 256))
+ {
+ sum += read_rc;
+ }
+
+ snprintf(msg, MSG_MAX, "connection closed, read %d bytes (%i)", sum, port);
+ debug(msg);
+ close(remote_sock);
+ kill(getppid(), SIGUSR1);
+ } while (cont);
+
+ close(listen_sock);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int rc;
+ int p;
+ char port_str[256];
+ char str_port_start[128];
+ char str_port_end[128];
+ char host_str[16] = "\0";
+ int start_port;
+ int end_port;
+ int opt;
+ char *delimiter;
+ struct sigaction sa;
+ struct sigaction sa2;
+
+ sa.sa_handler = &terminate_connections;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ sa2.sa_handler = &connection_counter;
+ sigaction(SIGUSR1, &sa2, NULL);
+
+ /* collect program args */
+ while ((opt = getopt(argc, argv, "p:a:dc")) != -1) {
+ switch (opt) {
+ case 'p':
+ strncpy(port_str, optarg, 256);
+ port_str[256-1]='\0';
+ delimiter = strchr(port_str, '-');
+ if (delimiter == NULL)
+ {
+ usage();
+ }
+ strncpy(str_port_start, port_str, delimiter - port_str);
+ str_port_start[delimiter - port_str] = '\0';
+ strncpy(str_port_end, delimiter+1, strlen(port_str) - (delimiter+1-port_str));
+ start_port = atoi(str_port_start);
+ end_port = atoi(str_port_end);
+ break;
+ case 'a':
+ strncpy(host_str, optarg, 64);
+ host_str[16-1] = '\0';
+ break;
+ case 'd':
+ debug_on = 1;
+ break;
+ case 'c':
+ cont = 1;
+ }
+ }
+
+ if (strlen(host_str) == 0)
+ {
+ usage();
+ return 1;
+ }
+
+ listener_count = 0;
+ /* spawn process to handle every port specified */
+ for (p = start_port; p < end_port; p++){
+ if ((rc = fork()) > 0)
+ {
+ /* parent, add pid to the list */
+ listeners[listener_count++] = rc;
+ }
+ else if (rc == 0)
+ {
+ /* child */
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ /* run the main processing loop */
+ handle_connections(host_str, p);
+ return 0;
+ }
+ }
+
+ /* gather children */
+ int child_status;
+ int i;
+ for (i=0; i < listener_count; i++)
+ {
+ int rc = -1;
+ while (wait(&child_status) == -1 && errno == EINTR)
+ {
+ ;
+ }
+ debug("worker finished");
+ }
+
+ debug("tcp_listener finished");
+
+ printf("handled %i connections\n", connection_count);
+
+ return 0;
+}