Blame SOURCES/iptables-1.4.21-restore_support_acquiring_the_lock.patch

43df5c
Adapted version of
43df5c
43df5c
commit 999eaa241212d3952ddff39a99d0d55a74e3639e
43df5c
Author: Lorenzo Colitti <lorenzo@google.com>
43df5c
Date:   Thu Mar 16 16:55:02 2017 +0900
43df5c
43df5c
    iptables-restore: support acquiring the lock.
43df5c
    
43df5c
    Currently, ip[6]tables-restore does not perform any locking, so it
43df5c
    is not safe to use concurrently with ip[6]tables.
43df5c
    
43df5c
    This patch makes ip[6]tables-restore wait for the lock if -w
43df5c
    was specified. Arguments to -w and -W are supported in the same
43df5c
    was as they are in ip[6]tables.
43df5c
    
43df5c
    The lock is not acquired on startup. Instead, it is acquired when
43df5c
    a new table handle is created (on encountering '*') and released
43df5c
    when the table is committed (COMMIT). This makes it possible to
43df5c
    keep long-running iptables-restore processes in the background
43df5c
    (for example, reading commands from a pipe opened by a system
43df5c
    management daemon) and simultaneously run iptables commands.
43df5c
    
43df5c
    If -w is not specified, then the command proceeds without taking
43df5c
    the lock.
43df5c
    
43df5c
    Tested as follows:
43df5c
    
43df5c
    1. Run iptables-restore -w, and check that iptables commands work
43df5c
       with or without -w.
43df5c
    2. Type "*filter" into the iptables-restore input. Verify that
43df5c
       a) ip[6]tables commands without -w fail with "another app is
43df5c
          currently holding the xtables lock...".
43df5c
       b) ip[6]tables commands with "-w 2" fail after 2 seconds.
43df5c
       c) ip[6]tables commands with "-w" hang until "COMMIT" is
43df5c
          typed into the iptables-restore window.
43df5c
    3. With the lock held by an ip6tables-restore process:
43df5c
         strace -e flock /tmp/iptables/sbin/iptables-restore -w 1 -W 100000
43df5c
       shows 11 calls to flock and fails.
43df5c
    4. Run an iptables-restore with -w and one without -w, and check:
43df5c
       a) Type "*filter" in the first and then the second, and the
43df5c
          second exits with an error.
43df5c
       b) Type "*filter" in the second and "*filter" "-S" "COMMIT"
43df5c
          into the first. The rules are listed only when the first
43df5c
          copy sees "COMMIT".
43df5c
    
43df5c
    Signed-off-by: Narayan Kamath <narayan@google.com>
43df5c
    Signed-off-by: Lorenzo Colitti <lorenzo@google.com>
43df5c
    Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
43df5c
43df5c
diff -up iptables-1.4.21/iptables/ip6tables.c.restore_support_acquiring_the_lock iptables-1.4.21/iptables/ip6tables.c
43df5c
--- iptables-1.4.21/iptables/ip6tables.c.restore_support_acquiring_the_lock	2017-04-05 14:55:52.561008864 +0200
43df5c
+++ iptables-1.4.21/iptables/ip6tables.c	2017-04-05 14:55:52.564008888 +0200
43df5c
@@ -1767,7 +1767,7 @@ int do_command6(int argc, char *argv[],
43df5c
 	generic_opt_check(command, cs.options);
43df5c
 
43df5c
 	/* Attempt to acquire the xtables lock */
43df5c
-	if (!restore && !xtables_lock(wait, &wait_interval)) {
43df5c
+	if (!restore && xtables_lock(wait, &wait_interval) == XT_LOCK_BUSY) {
43df5c
 		fprintf(stderr, "Another app is currently holding the xtables lock. ");
43df5c
 		if (wait == 0)
43df5c
 			fprintf(stderr, "Perhaps you want to use the -w option?\n");
43df5c
diff -up iptables-1.4.21/iptables/ip6tables-restore.c.restore_support_acquiring_the_lock iptables-1.4.21/iptables/ip6tables-restore.c
43df5c
--- iptables-1.4.21/iptables/ip6tables-restore.c.restore_support_acquiring_the_lock	2013-11-22 12:18:13.000000000 +0100
43df5c
+++ iptables-1.4.21/iptables/ip6tables-restore.c	2017-04-05 14:58:41.513393942 +0200
43df5c
@@ -15,6 +15,7 @@
43df5c
 #include <stdio.h>
43df5c
 #include <stdlib.h>
43df5c
 #include "ip6tables.h"
43df5c
+#include "xshared.h"
43df5c
 #include "xtables.h"
43df5c
 #include "libiptc/libip6tc.h"
43df5c
 #include "ip6tables-multi.h"
43df5c
@@ -25,18 +26,24 @@
43df5c
 #define DEBUGP(x, args...)
43df5c
 #endif
43df5c
 
43df5c
-static int binary = 0, counters = 0, verbose = 0, noflush = 0;
43df5c
+static int binary = 0, counters = 0, verbose = 0, noflush = 0, wait = 0;
43df5c
+
43df5c
+static struct timeval wait_interval = {
43df5c
+	.tv_sec	= 1,
43df5c
+};
43df5c
 
43df5c
 /* Keeping track of external matches and targets.  */
43df5c
 static const struct option options[] = {
43df5c
-	{.name = "binary",   .has_arg = false, .val = 'b'},
43df5c
-	{.name = "counters", .has_arg = false, .val = 'c'},
43df5c
-	{.name = "verbose",  .has_arg = false, .val = 'v'},
43df5c
-	{.name = "test",     .has_arg = false, .val = 't'},
43df5c
-	{.name = "help",     .has_arg = false, .val = 'h'},
43df5c
-	{.name = "noflush",  .has_arg = false, .val = 'n'},
43df5c
-	{.name = "modprobe", .has_arg = true,  .val = 'M'},
43df5c
-	{.name = "table",    .has_arg = true,  .val = 'T'},
43df5c
+	{.name = "binary",        .has_arg = 0, .val = 'b'},
43df5c
+	{.name = "counters",      .has_arg = 0, .val = 'c'},
43df5c
+	{.name = "verbose",       .has_arg = 0, .val = 'v'},
43df5c
+	{.name = "test",          .has_arg = 0, .val = 't'},
43df5c
+	{.name = "help",          .has_arg = 0, .val = 'h'},
43df5c
+	{.name = "noflush",       .has_arg = 0, .val = 'n'},
43df5c
+	{.name = "modprobe",      .has_arg = 1, .val = 'M'},
43df5c
+	{.name = "table",         .has_arg = 1, .val = 'T'},
43df5c
+	{.name = "wait",          .has_arg = 2, .val = 'w'},
43df5c
+	{.name = "wait-interval", .has_arg = 2, .val = 'W'},
43df5c
 	{NULL},
43df5c
 };
43df5c
 
43df5c
@@ -44,14 +51,16 @@ static void print_usage(const char *name
43df5c
 
43df5c
 static void print_usage(const char *name, const char *version)
43df5c
 {
43df5c
-	fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n"
43df5c
+	fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h] [-w secs] [-W usecs]\n"
43df5c
 			"	   [ --binary ]\n"
43df5c
 			"	   [ --counters ]\n"
43df5c
 			"	   [ --verbose ]\n"
43df5c
 			"	   [ --test ]\n"
43df5c
 			"	   [ --help ]\n"
43df5c
+			"	   [ --wait=<seconds>\n"
43df5c
+			"	   [ --wait-interval=<usecs>\n"
43df5c
 			"	   [ --noflush ]\n"
43df5c
-			"          [ --modprobe=<command>]\n", name);
43df5c
+			"	   [ --modprobe=<command>]\n", name);
43df5c
 
43df5c
 	exit(1);
43df5c
 }
43df5c
@@ -182,7 +191,7 @@ int ip6tables_restore_main(int argc, cha
43df5c
 {
43df5c
 	struct xtc_handle *handle = NULL;
43df5c
 	char buffer[10240];
43df5c
-	int c;
43df5c
+	int c, lock;
43df5c
 	char curtable[XT_TABLE_MAXNAMELEN + 1];
43df5c
 	FILE *in;
43df5c
 	int in_table = 0, testing = 0;
43df5c
@@ -190,6 +199,7 @@ int ip6tables_restore_main(int argc, cha
43df5c
 	const struct xtc_ops *ops = &ip6tc_ops;
43df5c
 
43df5c
 	line = 0;
43df5c
+	lock = XT_LOCK_NOT_ACQUIRED;
43df5c
 
43df5c
 	ip6tables_globals.program_name = "ip6tables-restore";
43df5c
 	c = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
43df5c
@@ -204,7 +214,7 @@ int ip6tables_restore_main(int argc, cha
43df5c
 	init_extensions6();
43df5c
 #endif
43df5c
 
43df5c
-	while ((c = getopt_long(argc, argv, "bcvthnM:T:", options, NULL)) != -1) {
43df5c
+	while ((c = getopt_long(argc, argv, "bcvthnwWM:T:", options, NULL)) != -1) {
43df5c
 		switch (c) {
43df5c
 			case 'b':
43df5c
 				binary = 1;
43df5c
@@ -225,6 +235,12 @@ int ip6tables_restore_main(int argc, cha
43df5c
 			case 'n':
43df5c
 				noflush = 1;
43df5c
 				break;
43df5c
+			case 'w':
43df5c
+				wait = parse_wait_time(argc, argv);
43df5c
+				break;
43df5c
+			case 'W':
43df5c
+				parse_wait_interval(argc, argv, &wait_interval);
43df5c
+				break;
43df5c
 			case 'M':
43df5c
 				xtables_modprobe_program = optarg;
43df5c
 				break;
43df5c
@@ -269,8 +285,23 @@ int ip6tables_restore_main(int argc, cha
43df5c
 				DEBUGP("Not calling commit, testing\n");
43df5c
 				ret = 1;
43df5c
 			}
43df5c
+
43df5c
+			/* Done with the current table, release the lock. */
43df5c
+			if (lock >= 0) {
43df5c
+				xtables_unlock(lock);
43df5c
+				lock = XT_LOCK_NOT_ACQUIRED;
43df5c
+			}
43df5c
+
43df5c
 			in_table = 0;
43df5c
 		} else if ((buffer[0] == '*') && (!in_table)) {
43df5c
+			/* Acquire a lock before we create a new table handle */
43df5c
+			lock = xtables_lock(wait, &wait_interval);
43df5c
+			if (lock == XT_LOCK_BUSY) {
43df5c
+				fprintf(stderr, "Another app is currently holding the xtables lock. "
43df5c
+					"Perhaps you want to use the -w option?\n");
43df5c
+				exit(RESOURCE_PROBLEM);
43df5c
+			}
43df5c
+
43df5c
 			/* New table */
43df5c
 			char *table;
43df5c
 
43df5c
diff -up iptables-1.4.21/iptables/iptables.c.restore_support_acquiring_the_lock iptables-1.4.21/iptables/iptables.c
43df5c
--- iptables-1.4.21/iptables/iptables.c.restore_support_acquiring_the_lock	2017-04-05 14:55:52.562008872 +0200
43df5c
+++ iptables-1.4.21/iptables/iptables.c	2017-04-05 14:55:52.564008888 +0200
43df5c
@@ -1754,7 +1754,7 @@ int do_command4(int argc, char *argv[],
43df5c
 	generic_opt_check(command, cs.options);
43df5c
 
43df5c
 	/* Attempt to acquire the xtables lock */
43df5c
-	if (!restore && !xtables_lock(wait, &wait_interval)) {
43df5c
+	if (!restore && xtables_lock(wait, &wait_interval) == XT_LOCK_BUSY) {
43df5c
 		fprintf(stderr, "Another app is currently holding the xtables lock. ");
43df5c
 		if (wait == 0)
43df5c
 			fprintf(stderr, "Perhaps you want to use the -w option?\n");
43df5c
diff -up iptables-1.4.21/iptables/iptables-restore.c.restore_support_acquiring_the_lock iptables-1.4.21/iptables/iptables-restore.c
43df5c
--- iptables-1.4.21/iptables/iptables-restore.c.restore_support_acquiring_the_lock	2013-11-22 12:18:13.000000000 +0100
43df5c
+++ iptables-1.4.21/iptables/iptables-restore.c	2017-04-05 15:00:17.389179935 +0200
43df5c
@@ -12,6 +12,7 @@
43df5c
 #include <stdio.h>
43df5c
 #include <stdlib.h>
43df5c
 #include "iptables.h"
43df5c
+#include "xshared.h"
43df5c
 #include "xtables.h"
43df5c
 #include "libiptc/libiptc.h"
43df5c
 #include "iptables-multi.h"
43df5c
@@ -22,18 +23,24 @@
43df5c
 #define DEBUGP(x, args...)
43df5c
 #endif
43df5c
 
43df5c
-static int binary = 0, counters = 0, verbose = 0, noflush = 0;
43df5c
+static int binary = 0, counters = 0, verbose = 0, noflush = 0, wait = 0;
43df5c
+
43df5c
+static struct timeval wait_interval = {
43df5c
+	.tv_sec	= 1,
43df5c
+};
43df5c
 
43df5c
 /* Keeping track of external matches and targets.  */
43df5c
 static const struct option options[] = {
43df5c
-	{.name = "binary",   .has_arg = false, .val = 'b'},
43df5c
-	{.name = "counters", .has_arg = false, .val = 'c'},
43df5c
-	{.name = "verbose",  .has_arg = false, .val = 'v'},
43df5c
-	{.name = "test",     .has_arg = false, .val = 't'},
43df5c
-	{.name = "help",     .has_arg = false, .val = 'h'},
43df5c
-	{.name = "noflush",  .has_arg = false, .val = 'n'},
43df5c
-	{.name = "modprobe", .has_arg = true,  .val = 'M'},
43df5c
-	{.name = "table",    .has_arg = true,  .val = 'T'},
43df5c
+	{.name = "binary",        .has_arg = 0, .val = 'b'},
43df5c
+	{.name = "counters",      .has_arg = 0, .val = 'c'},
43df5c
+	{.name = "verbose",       .has_arg = 0, .val = 'v'},
43df5c
+	{.name = "test",          .has_arg = 0, .val = 't'},
43df5c
+	{.name = "help",          .has_arg = 0, .val = 'h'},
43df5c
+	{.name = "noflush",       .has_arg = 0, .val = 'n'},
43df5c
+	{.name = "modprobe",      .has_arg = 1, .val = 'M'},
43df5c
+	{.name = "table",         .has_arg = 1, .val = 'T'},
43df5c
+	{.name = "wait",          .has_arg = 2, .val = 'w'},
43df5c
+	{.name = "wait-interval", .has_arg = 2, .val = 'W'},
43df5c
 	{NULL},
43df5c
 };
43df5c
 
43df5c
@@ -43,15 +50,17 @@ static void print_usage(const char *name
43df5c
 
43df5c
 static void print_usage(const char *name, const char *version)
43df5c
 {
43df5c
-	fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n"
43df5c
+	fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h] [-W usecs]\n"
43df5c
 			"	   [ --binary ]\n"
43df5c
 			"	   [ --counters ]\n"
43df5c
 			"	   [ --verbose ]\n"
43df5c
 			"	   [ --test ]\n"
43df5c
 			"	   [ --help ]\n"
43df5c
 			"	   [ --noflush ]\n"
43df5c
+			"	   [ --wait=<seconds>\n"
43df5c
+			"	   [ --wait-interval=<usecs>\n"
43df5c
 			"	   [ --table= ]\n"
43df5c
-			"          [ --modprobe=<command>]\n", name);
43df5c
+			"	   [ --modprobe=<command>]\n", name);
43df5c
 
43df5c
 	exit(1);
43df5c
 }
43df5c
@@ -182,7 +191,7 @@ iptables_restore_main(int argc, char *ar
43df5c
 {
43df5c
 	struct xtc_handle *handle = NULL;
43df5c
 	char buffer[10240];
43df5c
-	int c;
43df5c
+	int c, lock;
43df5c
 	char curtable[XT_TABLE_MAXNAMELEN + 1];
43df5c
 	FILE *in;
43df5c
 	int in_table = 0, testing = 0;
43df5c
@@ -190,6 +199,7 @@ iptables_restore_main(int argc, char *ar
43df5c
 	const struct xtc_ops *ops = &iptc_ops;
43df5c
 
43df5c
 	line = 0;
43df5c
+	lock = XT_LOCK_NOT_ACQUIRED;
43df5c
 
43df5c
 	iptables_globals.program_name = "iptables-restore";
43df5c
 	c = xtables_init_all(&iptables_globals, NFPROTO_IPV4);
43df5c
@@ -204,7 +214,7 @@ iptables_restore_main(int argc, char *ar
43df5c
 	init_extensions4();
43df5c
 #endif
43df5c
 
43df5c
-	while ((c = getopt_long(argc, argv, "bcvthnM:T:", options, NULL)) != -1) {
43df5c
+	while ((c = getopt_long(argc, argv, "bcvthnwWM:T:", options, NULL)) != -1) {
43df5c
 		switch (c) {
43df5c
 			case 'b':
43df5c
 				binary = 1;
43df5c
@@ -225,6 +235,12 @@ iptables_restore_main(int argc, char *ar
43df5c
 			case 'n':
43df5c
 				noflush = 1;
43df5c
 				break;
43df5c
+			case 'w':
43df5c
+				wait = parse_wait_time(argc, argv);
43df5c
+				break;
43df5c
+			case 'W':
43df5c
+				parse_wait_interval(argc, argv, &wait_interval);
43df5c
+				break;
43df5c
 			case 'M':
43df5c
 				xtables_modprobe_program = optarg;
43df5c
 				break;
43df5c
@@ -269,8 +285,23 @@ iptables_restore_main(int argc, char *ar
43df5c
 				DEBUGP("Not calling commit, testing\n");
43df5c
 				ret = 1;
43df5c
 			}
43df5c
+
43df5c
+			/* Done with the current table, release the lock. */
43df5c
+			if (lock >= 0) {
43df5c
+				xtables_unlock(lock);
43df5c
+				lock = XT_LOCK_NOT_ACQUIRED;
43df5c
+			}
43df5c
+
43df5c
 			in_table = 0;
43df5c
 		} else if ((buffer[0] == '*') && (!in_table)) {
43df5c
+			/* Acquire a lock before we create a new table handle */
43df5c
+			lock = xtables_lock(wait, &wait_interval);
43df5c
+			if (lock == XT_LOCK_BUSY) {
43df5c
+				fprintf(stderr, "Another app is currently holding the xtables lock. "
43df5c
+					"Perhaps you want to use the -w option?\n");
43df5c
+				exit(RESOURCE_PROBLEM);
43df5c
+			}
43df5c
+
43df5c
 			/* New table */
43df5c
 			char *table;
43df5c
 
43df5c
diff -up iptables-1.4.21/iptables/xshared.c.restore_support_acquiring_the_lock iptables-1.4.21/iptables/xshared.c
43df5c
--- iptables-1.4.21/iptables/xshared.c.restore_support_acquiring_the_lock	2017-04-05 14:55:52.562008872 +0200
43df5c
+++ iptables-1.4.21/iptables/xshared.c	2017-04-05 14:55:52.565008896 +0200
43df5c
@@ -246,7 +246,7 @@ void xs_init_match(struct xtables_match
43df5c
 		match->init(match->m);
43df5c
 }
43df5c
 
43df5c
-bool xtables_lock(int wait, struct timeval *wait_interval)
43df5c
+int xtables_lock(int wait, struct timeval *wait_interval)
43df5c
 {
43df5c
 	struct timeval time_left, wait_time;
43df5c
 	int fd, i = 0;
43df5c
@@ -256,22 +256,22 @@ bool xtables_lock(int wait, struct timev
43df5c
 
43df5c
 	fd = open(XT_LOCK_NAME, O_CREAT, 0600);
43df5c
 	if (fd < 0)
43df5c
-		return true;
43df5c
+		return XT_LOCK_UNSUPPORTED;
43df5c
 
43df5c
 	if (wait == -1) {
43df5c
 		if (flock(fd, LOCK_EX) == 0)
43df5c
-			return true;
43df5c
+			return fd;
43df5c
 
43df5c
 		fprintf(stderr, "Can't lock %s: %s\n", XT_LOCK_NAME,
43df5c
 			strerror(errno));
43df5c
-		return false;
43df5c
+		return XT_LOCK_BUSY;
43df5c
 	}
43df5c
 
43df5c
 	while (1) {
43df5c
 		if (flock(fd, LOCK_EX | LOCK_NB) == 0)
43df5c
-			return true;
43df5c
+			return fd;
43df5c
 		else if (timercmp(&time_left, wait_interval, <))
43df5c
-			return false;
43df5c
+			return XT_LOCK_BUSY;
43df5c
 
43df5c
 		if (++i % 10 == 0) {
43df5c
 			fprintf(stderr, "Another app is currently holding the xtables lock; "
43df5c
@@ -285,6 +285,12 @@ bool xtables_lock(int wait, struct timev
43df5c
 	}
43df5c
 }
43df5c
 
43df5c
+void xtables_unlock(int lock)
43df5c
+{
43df5c
+	if (lock >= 0)
43df5c
+		close(lock);
43df5c
+}
43df5c
+
43df5c
 int parse_wait_time(int argc, char *argv[])
43df5c
 {
43df5c
 	int wait = -1;
43df5c
diff -up iptables-1.4.21/iptables/xshared.h.restore_support_acquiring_the_lock iptables-1.4.21/iptables/xshared.h
43df5c
--- iptables-1.4.21/iptables/xshared.h.restore_support_acquiring_the_lock	2017-04-05 14:55:52.562008872 +0200
43df5c
+++ iptables-1.4.21/iptables/xshared.h	2017-04-05 14:55:52.565008896 +0200
43df5c
@@ -84,7 +84,28 @@ extern struct xtables_match *load_proto(
43df5c
 extern int subcmd_main(int, char **, const struct subcommand *);
43df5c
 extern void xs_init_target(struct xtables_target *);
43df5c
 extern void xs_init_match(struct xtables_match *);
43df5c
-bool xtables_lock(int wait, struct timeval *wait_interval);
43df5c
+
43df5c
+/**
43df5c
+ * Values for the iptables lock.
43df5c
+ *
43df5c
+ * A value >= 0 indicates the lock filedescriptor. Other values are:
43df5c
+ *
43df5c
+ * XT_LOCK_UNSUPPORTED : The system does not support locking, execution will
43df5c
+ * proceed lockless.
43df5c
+ *
43df5c
+ * XT_LOCK_BUSY : The lock was held by another process. xtables_lock only
43df5c
+ * returns this value when |wait| == false. If |wait| == true, xtables_lock
43df5c
+ * will not return unless the lock has been acquired.
43df5c
+ *
43df5c
+ * XT_LOCK_NOT_ACQUIRED : We have not yet attempted to acquire the lock.
43df5c
+ */
43df5c
+enum {
43df5c
+	XT_LOCK_BUSY = -1,
43df5c
+	XT_LOCK_UNSUPPORTED  = -2,
43df5c
+	XT_LOCK_NOT_ACQUIRED  = -3,
43df5c
+};
43df5c
+extern int xtables_lock(int wait, struct timeval *tv);
43df5c
+extern void xtables_unlock(int lock);
43df5c
 
43df5c
 int parse_wait_time(int argc, char *argv[]);
43df5c
 void parse_wait_interval(int argc, char *argv[], struct timeval *wait_interval);