[netfilter-cvslog] r3482 - in trunk/nfsim: core tools

rusty at netfilter.org rusty at netfilter.org
Wed Dec 22 02:05:20 CET 2004


Author: rusty at netfilter.org
Date: 2004-12-22 02:05:19 +0100 (Wed, 22 Dec 2004)
New Revision: 3482

Modified:
   trunk/nfsim/core/core.h
   trunk/nfsim/core/failtest.c
   trunk/nfsim/core/message.c
   trunk/nfsim/core/message.h
   trunk/nfsim/tools/info.c
   trunk/nfsim/tools/iptables.c
Log:
Major rework of message system, to handle forking while talking to iptables:
	Fix --failpattern: check suppress before failpattern, for init code.
	fflush(stdout) before forking, otherwise duplicated output.
	Add start_program/end_program/fork_other_program to message.c
	Add should_i_fail() calls to copy_from_user and copy_to_user.
	add_message() and replay_messages() to "fork" iptables (alternate: pass file descriptors, which would be faster).
	wait_for_message() now wait_for_output(): returns output directly.
	Add "info failpoints" to see how many things could fail.


Modified: trunk/nfsim/core/core.h
===================================================================
--- trunk/nfsim/core/core.h	2004-12-22 00:57:57 UTC (rev 3481)
+++ trunk/nfsim/core/core.h	2004-12-22 01:05:19 UTC (rev 3482)
@@ -279,6 +279,7 @@
 bool should_i_fail_once(const char *location);
 bool get_failtest(void);
 extern bool suppress_failtest;
+extern unsigned int failpoints;
 
 /* Root for all kernel code allocations (so we check memory leaks) */
 extern void *nfsim_tallocs;

Modified: trunk/nfsim/core/failtest.c
===================================================================
--- trunk/nfsim/core/failtest.c	2004-12-22 00:57:57 UTC (rev 3481)
+++ trunk/nfsim/core/failtest.c	2004-12-22 01:05:19 UTC (rev 3482)
@@ -28,7 +28,8 @@
 
 static bool failtest = false;
 bool suppress_failtest;
-static int generations = 0;
+static unsigned int generations = 0;
+unsigned int failpoints = 0;
 
 /* Failures pattern so far. */
 static char *faillist;
@@ -119,6 +120,9 @@
 {
 	char *p;
 
+	if (suppress_failtest)
+		return false;
+
 	if (failpattern) {
 		p = strstr(orig_failpattern ?: "", location);
 		if (p && p <= failpattern
@@ -142,12 +146,17 @@
 	pid_t child;
 	int status;
 
+	if (suppress_failtest)
+		return false;
+
 	if (failpattern)
 		return do_failpattern(func);
 
-	if (!failtest || suppress_failtest)
+	failpoints++;
+	if (!failtest)
 		return false;
 
+	fflush(stdout);
 	child = fork();
 	if (child == -1)
 		barf_perror("fork failed for failtest!");
@@ -155,7 +164,9 @@
 	/* The child actually fails.  The script will screw up at this
 	 * point, but should not crash. */
 	if (child == 0) {
+		/* If we're talking to iptables, it has to fork too. */
 		faillist = talloc_asprintf_append(faillist, "[%s]F", func);
+		fork_other_program();
 		if (++generations > 128)
 			barf("Too deep recursion on failure path:\n%s",
 			     faillist);

Modified: trunk/nfsim/core/message.c
===================================================================
--- trunk/nfsim/core/message.c	2004-12-22 00:57:57 UTC (rev 3481)
+++ trunk/nfsim/core/message.c	2004-12-22 01:05:19 UTC (rev 3482)
@@ -28,11 +28,12 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <sys/wait.h>
 
 static int sock[2]; /* socket for messages. 0 = parent, 1 = child */
 
-static int send_userspace_message(struct nf_userspace_message *msg);
-static int handle_userspace_message(int fd);
+static void send_userspace_message(struct nf_userspace_message *msg);
+static void handle_userspace_message(void);
 
 void message_init(void)
 {
@@ -68,70 +69,207 @@
 	sigprocmask(SIG_UNBLOCK, &ss, NULL);
 }
 
-int handle_userspace_message(int fd)
+struct message
 {
+	struct list_head list;
+	bool outgoing;
+	struct nf_userspace_message msg;
+	char data[0];
+};
+
+struct program
+{
+	char *name;
+	struct list_head messages;
+	pid_t pid;
+	int fd;
+	int argc;
+	char *argv[0];
+};
+static struct program *currprog;
+
+void start_program(const char *name, int argc, char *argv[])
+{
+	int i, childfd[2];
+
+	assert(!currprog);
+	currprog = talloc_size(NULL, sizeof(*currprog)
+			       + (argc+1)*sizeof(char *));
+
+	currprog->name = talloc_strdup(currprog, name);
+	currprog->argc = argc;
+	for (i = 0; i < argc; i++)
+		currprog->argv[i] = talloc_strdup(currprog, argv[i]);
+	currprog->argv[argc] = NULL;
+	INIT_LIST_HEAD(&currprog->messages);
+
+	if (pipe(childfd) != 0)
+		barf_perror("%s pipe", currprog->name);
+
+	fflush(stdout);
+	currprog->pid = fork();
+	switch (currprog->pid) {
+	case -1:
+		barf_perror("iptables fork");
+	case 0:
+		dup2(childfd[1], STDOUT_FILENO);
+		dup2(childfd[1], STDERR_FILENO);
+		if (setenv("LD_PRELOAD", "fakesockopt.so.1.0", 1))
+			barf("putenv failed");
+		execvp(currprog->name, currprog->argv);
+		fprintf(stderr, "Could not exec %s!\n", currprog->name);
+		exit(EXIT_FAILURE);
+	}
+
+	close(childfd[1]);
+	currprog->fd = childfd[0];
+}
+
+int end_program(const char *name)
+{
+	int status;
+
+	if (waitpid(currprog->pid, &status, 0) <= 0)
+		barf_perror("Waiting for child %s", name);
+
+	close(currprog->fd);
+	talloc_free(currprog);
+	currprog = NULL;
+
+	return status;
+}
+
+static void add_message(bool outgoing, struct nf_userspace_message *msg,
+			int datalen, const void *data)
+{
+	struct message *new;
+
+	assert(currprog);
+	new = talloc_size(currprog, sizeof(*new) + datalen);
+	new->outgoing = outgoing;
+	new->msg = *msg;
+	memcpy(new->data, data, datalen);
+	list_add_tail(&new->list, &currprog->messages);
+}
+
+static void replay_messages(struct program *old)
+{
+	struct message *i, *in;
+	int totlen;
+
+	list_for_each_entry(i, &old->messages, list) {
+		add_message(i->outgoing, &i->msg, i->msg.len, &i->msg+1);
+		totlen = sizeof(i->msg) + i->msg.len;
+		if (i->outgoing) {
+			write(sock[0], &i->msg, totlen);
+			continue;
+		}
+
+		in = talloc_size(NULL, sizeof(*in) + i->msg.len);
+		/* Read in two parts: can be written that way. */
+		if (read(sock[0], &in->msg, sizeof(in->msg)) != sizeof(in->msg)
+		    || read(sock[0], in->data, i->msg.len) != i->msg.len)
+			barf_perror("read during replay");
+		if (memcmp(&in->msg, &i->msg, totlen) != 0)
+			barf_perror("replay turned out different");
+		talloc_free(in);
+	}
+}
+
+void handle_userspace_message(void)
+{
 	int len;
 	struct nf_userspace_message msg;
 
 	/* FIXME: msg.len?  Presumable always 0? --RR */
-	len = read(fd, &msg, sizeof(msg));
+	len = read(sock[0], &msg, sizeof(msg));
 	if (len != sizeof(msg))
-		return -1;
+		barf_perror("reading userspace message");
 
+#if 0
+	fprintf(stderr, "nfsim: %i received %s\n",
+		getpid(),
+		msg.type == UM_SYSCALL ? 
+		(msg.opcode == SYS_GETSOCKOPT ? "GETSOCKOPT" : 
+		 msg.opcode == SYS_SETSOCKOPT ? "SETSOCKOPT" : "SYS???") :
+		(msg.opcode == KOP_COPY_FROM_USER ? "COPY_FROM_USER" :
+		 msg.opcode == KOP_COPY_TO_USER ? "COPY_TO_USER" : "KOP???"));
+#endif
+
 	if (msg.type == UM_SYSCALL) {
+		add_message(false, &msg, 0, NULL);
 		switch (msg.opcode) {
 
 		case SYS_GETSOCKOPT:
 			msg.retval = nf_getsockopt(NULL, msg.args[0],
 					msg.args[1], (char *)msg.args[2],
 					&msg.args[3]);
-			write(fd, &msg, sizeof(struct nf_userspace_message) +
-				msg.len);
+#if 0
+			fprintf(stderr, "nfsim: %i sent GETSOCKOPT %i\n",
+				getpid(), msg.retval);
+#endif
+			write(sock[0], &msg, sizeof(msg));
+			add_message(true, &msg, 0, NULL);
 			break;
 		case SYS_SETSOCKOPT:
 			msg.retval = nf_setsockopt(NULL, msg.args[0],
 					msg.args[1], (char *)msg.args[2],
 					msg.args[3]);
-			write(fd, &msg, sizeof(struct nf_userspace_message) +
-				msg.len);
+#if 0
+			fprintf(stderr, "nfsim: %i sent SETSOCKOPT %i\n",
+				getpid(), msg.retval);
+#endif
+			write(sock[0], &msg, sizeof(msg));
+			add_message(true, &msg, 0, NULL);
 			break;
 		default:
-			fprintf(stderr, "Invalid syscall opcode %d\n",
-				msg.opcode);
+			barf("Invalid syscall opcode %d\n", msg.opcode);
 		}
 	} else if (msg.type == UM_KERNELOP) {
 		switch (msg.opcode) {
 
 		case KOP_COPY_FROM_USER:
-			if (complete_read(fd, (char *)msg.args[0],
+			if (complete_read(sock[0], (char *)msg.args[0],
 					msg.args[2]) !=	msg.args[2])
 				barf_perror("read");
+			add_message(false, &msg, msg.args[2],
+				    (char *)msg.args[0]);
 			break;
 		case KOP_COPY_TO_USER:
+			add_message(false, &msg, 0, NULL);
 			/* copying has been done */
 			break;
 		default:
-			fprintf(stderr, "Invalid kernelop opcode %d\n",
-				msg.opcode);
+			barf("Invalid kernelop opcode %d\n", msg.opcode);
 		}
-	} else {
-		fprintf(stderr, "Unknown message type %d\n", msg.type);
-	}
-
-	return 0;
+	} else
+		barf("Unknown message type %d\n", msg.type);
 }
 
-int send_userspace_message(struct nf_userspace_message *msg)
+void send_userspace_message(struct nf_userspace_message *msg)
 {
+#if 0
+	fprintf(stderr, "nfsim: %i sent %s\n",
+		getpid(),
+		msg->type == UM_SYSCALL ? 
+		(msg->opcode == SYS_GETSOCKOPT ? "GETSOCKOPT" : 
+		 msg->opcode == SYS_SETSOCKOPT ? "SETSOCKOPT" : "SYS???") :
+		(msg->opcode == KOP_COPY_FROM_USER ? "COPY_FROM_USER" :
+		 msg->opcode == KOP_COPY_TO_USER ? "COPY_TO_USER" : "KOP??"));
+#endif
 	write(sock[0], msg, sizeof(struct nf_userspace_message) + msg->len);
+	add_message(true, msg, msg->len, msg+1);
 	/* expect a reply */
-	return handle_userspace_message(sock[0]);
+	handle_userspace_message();
 }
 
 int copy_to_user(void *to, const void *from, unsigned long n)
 {
 	struct nf_userspace_message *msg;
 
+	if (should_i_fail(__func__))
+		return n;
+
 	msg = talloc_zero_named_const(NULL,
 		sizeof(struct nf_userspace_message) + n, "copy_to_user");
 
@@ -159,6 +297,9 @@
 {
 	struct nf_userspace_message msg;
 
+	if (should_i_fail(__func__))
+		return n;
+
 	memset(&msg, 0, sizeof(struct nf_userspace_message));
 
 	msg.type = UM_KERNELOP;
@@ -177,20 +318,43 @@
 	return 0;
 }
 
-/* Loop accepting messages from fakesockopt: stop when message on extra_fd */
-void wait_for_message(int extra_fd)
+/* This child wants a fresh program to play with */
+void fork_other_program(void)
 {
+	struct program *old;
+
+	/* Guarantee new socket for this child. */
+	message_cleanup();
+	message_init();
+
+	if (!currprog)
+		return;
+
+	old = currprog;
+	currprog = NULL;
+	start_program(old->name, old->argc, old->argv);
+	replay_messages(old);
+	talloc_free(old);
+}
+
+/* Loop accepting messages from fakesockopt.  If child talks, return. */
+char *wait_for_output(void)
+{
 	fd_set fdset;
 	int retry = 0;
 
-	do {
+	for (;;) {
 		int maxfd, sret;
 
 		FD_ZERO(&fdset);
-		FD_SET(extra_fd, &fdset);
 		FD_SET(sock[0], &fdset);
 
-		maxfd = max(extra_fd, sock[0]);
+		maxfd = sock[0];
+		if (currprog) {
+			FD_SET(currprog->fd, &fdset);
+			if (currprog->fd > maxfd)
+				maxfd = currprog->fd;
+		}
 		sret = select(maxfd + 1, &fdset, NULL, NULL, NULL);
 		if (sret < 0) {
 			if ((errno == EINTR || errno == EAGAIN)
@@ -200,6 +364,17 @@
 		}
 
 		if (FD_ISSET(sock[0], &fdset))
-			handle_userspace_message(sock[0]);
-	} while (!FD_ISSET(extra_fd, &fdset));
+			handle_userspace_message();
+
+		if (currprog && FD_ISSET(currprog->fd, &fdset)) {
+			char *output = talloc_size(NULL, 1024);
+			int ret = read(currprog->fd, output, 1023);
+			if (ret > 0) {
+				output[ret] = '\0';
+				return output;
+			}
+			talloc_free(output);
+			return NULL;
+		}
+	}
 }

Modified: trunk/nfsim/core/message.h
===================================================================
--- trunk/nfsim/core/message.h	2004-12-22 00:57:57 UTC (rev 3481)
+++ trunk/nfsim/core/message.h	2004-12-22 01:05:19 UTC (rev 3482)
@@ -31,5 +31,14 @@
 int copy_to_user(void *to, const void *from, unsigned long n);
 int copy_from_user(void *to, const void *from, unsigned long n);
 
-void wait_for_message(int extra_fd);
+/* Returns talloced output of child (if running). */
+char *wait_for_output(void);
+
+/* We want to fork: split other program */
+void fork_other_program(void);
+
+/* Start a program*/
+void start_program(const char *name, int argc, char *argv[]);
+int end_program(const char *name);
+
 #endif /* __HAVE_MESSAGE_H */

Modified: trunk/nfsim/tools/info.c
===================================================================
--- trunk/nfsim/tools/info.c	2004-12-22 00:57:57 UTC (rev 3481)
+++ trunk/nfsim/tools/info.c	2004-12-22 01:05:19 UTC (rev 3482)
@@ -121,12 +121,24 @@
         <para>the contents of the kernel routing cache</para>
        </listitem>
       </varlistentry>
+      <varlistentry>
+       <term>failpoints</term>
+       <listitem>
+        <para>show how many failure points have been passed: with --failtest, each of these alternatives would be attempted.</para>
+       </listitem>
+      </varlistentry>
      </variablelist>
 
     </section>
 */
 }
 
+static bool print_failpoints(int argc, char **argv)
+{
+	nfsim_log(LOG_ALWAYS, "failpoints: %u", failpoints);
+	return true;
+}
+
 static bool info(int argc, char **argv)
 {
 	if (argc == 1) {
@@ -145,6 +157,8 @@
 		return rcaches(argc, argv);
 	else if (streq(argv[0], "timers"))
 		return timers(argc, argv);
+	else if (streq(argv[0], "failpoints"))
+		return print_failpoints(argc, argv);
 
 
 	nfsim_log(LOG_ALWAYS, "Unknown info command: %s", argv[0]);

Modified: trunk/nfsim/tools/iptables.c
===================================================================
--- trunk/nfsim/tools/iptables.c	2004-12-22 00:57:57 UTC (rev 3481)
+++ trunk/nfsim/tools/iptables.c	2004-12-22 01:05:19 UTC (rev 3482)
@@ -31,66 +31,29 @@
 #include "utils.h"
 #include "message.h"
 
-/* FIXME: We can't fork while we're talking to the external program.
- * The correct thing to do is to record the messages up to the point
- * of fork in failtest.c, do a new run_command() and replay the
- * messages, which effectively "forks" the external program.
- */
 static bool run_command(int argc, char **argv)
 {
-	pid_t child;
 	int status;
-	int childfd[2];
-	char *prefix;
+	char *prefix, *name, *output;
 	char buf[4096] = { 0 };
 
-	suppress_failtest = true;
+	prefix = getenv("NFSIM_IPTABLES_PREFIX");
+	if (prefix)
+		name = talloc_asprintf(NULL, "%s/%s", prefix, argv[0]);
+	else
+		name = talloc_strdup(NULL, argv[0]);
 
-	if (pipe(childfd) != 0)
-		barf_perror("%s pipe", argv[0]);
+	start_program(name, argc, argv);
 
-	child = fork();
-	switch (child) {
-	case -1:
-		barf_perror("iptables fork");
-	case 0:
-		dup2(childfd[1], STDOUT_FILENO);
-		dup2(childfd[1], STDERR_FILENO);
-		if (setenv("LD_PRELOAD", "fakesockopt.so.1.0", 1))
-			barf("putenv failed");
-		prefix = getenv("NFSIM_IPTABLES_PREFIX");
-		if (prefix) {
-			char *path = talloc_asprintf(NULL, "%s/%s", prefix,
-						     argv[0]);
-			execv(path, argv);
-			fprintf(stderr, "Could not exec %s!\n", path);
-			talloc_free(path);
-		} else {
-			execvp(argv[0], argv);
-			fprintf(stderr, "Could not exec %s!\n", argv[0]);
-		}
-		exit(EXIT_FAILURE);
-	}
-
-	close(childfd[1]);
-	for (;;) {
-		char input[1024];
-		int ret;
+	while ((output = wait_for_output()) != NULL) {
 		/* Take output from iptables, and feed through logging. */
-		wait_for_message(childfd[0]);
-		ret = read(childfd[0], input, sizeof(input)-1);
-		if (ret <= 0)
-			break;
-		input[ret] = '\0';
-		nfsim_log_partial(LOG_USERSPACE, buf, sizeof(buf), "%s",input);
+		nfsim_log_partial(LOG_USERSPACE, buf, sizeof(buf), "%s",
+				  output);
+		talloc_free(output);
 	}
-	suppress_failtest = false;
+	status = end_program(name);
+	talloc_free(name);
 
-	if (waitpid(child, &status, 0) <= 0)
-		barf_perror("Waiting for %s child", argv[0]);
-
-	close(childfd[0]);
-
 	if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
 		return true;
 	return false;




More information about the netfilter-cvslog mailing list