add --atomic options
diff --git a/communication.c b/communication.c
index 8599d48..3dee991 100644
--- a/communication.c
+++ b/communication.c
@@ -27,7 +27,7 @@
 
 int sockfd = -1;
 
-void get_sockfd()
+static void get_sockfd()
 {
 	if (sockfd == -1) {
 		sockfd = socket(AF_INET, SOCK_RAW, PF_INET);
@@ -209,6 +209,47 @@
 	return new;
 }
 
+static void store_table_in_file(char *filename, struct ebt_replace *repl)
+{
+	char *command, *data;
+	int size;
+	FILE *file;
+
+	// start from an empty file with right priviliges
+	command = (char *)malloc(strlen(filename) + 15);
+	if (!command)
+		print_memory();
+	strcpy(command, "cat /dev/null>");
+	strcpy(command + 14, filename);
+	if (system(command))
+		print_error("Couldn't create file %s", filename);
+	strcpy(command, "chmod 600 ");
+	strcpy(command + 10, filename);
+	if (system(command))
+		print_error("Couldn't chmod file %s", filename);
+	free(command);
+
+	size = sizeof(struct ebt_replace) + repl->entries_size +
+	   repl->nentries * sizeof(struct ebt_counter);
+	data = (char *)malloc(size);
+	if (!data)
+		print_memory();
+	memcpy(data, repl, sizeof(struct ebt_replace));
+	memcpy(data + sizeof(struct ebt_replace), repl->entries,
+	   repl->entries_size);
+	// initialize counters to zero, deliver_counters() can update them
+	memset(data + sizeof(struct ebt_replace) + repl->entries_size,
+	   0, repl->nentries * sizeof(struct ebt_counter));
+	if (!(file = fopen(filename, "wb")))
+		print_error("Could not open file %s", filename);
+	if (fwrite(data, sizeof(char), size, file) != size) {
+		fclose(file);
+		print_error("Could not write everything to file %s", filename);
+	}
+	fclose(file);
+	free(data);
+}
+
 void deliver_table(struct ebt_u_replace *u_repl)
 {
 	socklen_t optlen;
@@ -216,15 +257,43 @@
 
 	// translate the struct ebt_u_replace to a struct ebt_replace
 	repl = translate_user2kernel(u_repl);
-	get_sockfd();
 	// give the data to the kernel
 	optlen = sizeof(struct ebt_replace) + repl->entries_size;
+	if (u_repl->filename != NULL) {
+		store_table_in_file(u_repl->filename, repl);
+		return;
+	}
+	get_sockfd();
 	if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen))
 		print_error("The kernel doesn't support a certain ebtables"
 		  " extension, consider recompiling your kernel or insmod"
 		  " the extension");	
 }
 
+static void store_counters_in_file(char *filename, struct ebt_u_replace *repl)
+{
+	int size = repl->nentries * sizeof(struct ebt_counter);
+	int entries_size;
+	struct ebt_replace hlp;
+	FILE *file;
+
+	if (!(file = fopen(filename, "r+b")))
+		print_error("Could not open file %s", filename);
+	// find out entries_size and then set the file pointer to the counters
+	if (fseek(file, (char *)(&hlp.entries_size) - (char *)(&hlp), SEEK_SET)
+	   || fread(&entries_size, sizeof(char), sizeof(unsigned int), file) !=
+	   sizeof(unsigned int) ||
+	   fseek(file, entries_size + sizeof(struct ebt_replace), SEEK_SET)) {
+		fclose(file);
+		print_error("File %s is corrupt", filename);
+	}
+	if (fwrite(repl->counters, sizeof(char), size, file) != size) {
+		fclose(file);
+		print_error("Could not write everything to file %s", filename);
+	}
+	fclose(file);
+}
+
 // gets executed after deliver_table
 void
 deliver_counters(struct ebt_u_replace *u_repl, unsigned short *counterchanges)
@@ -273,6 +342,10 @@
 	free(u_repl->counters);
 	u_repl->counters = newcounters;
 	u_repl->num_counters = u_repl->nentries;
+	if (u_repl->filename != NULL) {
+		store_counters_in_file(u_repl->filename, u_repl);
+		return;
+	}
 	optlen = u_repl->nentries * sizeof(struct ebt_counter) +
 	   sizeof(struct ebt_replace);
 	// now put the stuff in the kernel's struct ebt_replace
@@ -484,37 +557,109 @@
 	return 0;
 }
 
+static void retrieve_from_file(char *filename, struct ebt_replace *repl,
+   char command)
+{
+	FILE *file;
+	char *hlp;
+	int size;
+
+	if (!(file = fopen(filename, "r+b")))
+		print_error("Could not open file %s", filename);
+	// make sure table name is right if command isn't -L or --atomic-commit
+	if (command != 'L' && command != 8) {
+		hlp = (char *)malloc(strlen(repl->name));
+		if (!hlp)
+			print_memory();
+		strcpy(hlp, repl->name);
+	} else
+		if (!find_table(repl->name))
+			print_error("File %s contains invalid table name",
+			   filename);
+	if (fread(repl, sizeof(char), sizeof(struct ebt_replace), file)
+	   != sizeof(struct ebt_replace))
+		print_error("File %s is corrupt", filename);
+	if (command != 'L' && command != 8 && strcmp(hlp, repl->name))
+		print_error("File %s contains wrong table name or is corrupt",
+		   filename);
+	size = sizeof(struct ebt_replace) +
+	   repl->nentries * sizeof(struct ebt_counter) + repl->entries_size;
+	fseek(file, 0, SEEK_END);
+	if (size != ftell(file))
+		print_error("File %s has wrong size", filename);
+	repl->entries = (char *)malloc(repl->entries_size);
+	if (!repl->entries)
+		print_memory();
+	if (repl->nentries) {
+		repl->counters = (struct ebt_counter *)
+		   malloc(repl->nentries * sizeof(struct ebt_counter));
+		if (!repl->counters)
+			print_memory();
+	} else
+		repl->counters = NULL;
+	// copy entries and counters
+	if (fseek(file, sizeof(struct ebt_replace), SEEK_SET) ||
+	   fread(repl->entries, sizeof(char), repl->entries_size, file)
+	   != repl->entries_size ||
+	   fseek(file, sizeof(struct ebt_replace) + repl->entries_size, SEEK_SET)
+	   || fread(repl->counters, sizeof(char),
+	   repl->nentries * sizeof(struct ebt_counter), file)
+	   != repl->nentries * sizeof(struct ebt_counter))
+		print_error("File %s is corrupt", filename);
+	fclose(file);
+}
+
+static int retrieve_from_kernel(struct ebt_replace *repl, char command)
+{
+	socklen_t optlen;
+	int optname;
+
+	optlen = sizeof(struct ebt_replace);
+	get_sockfd();
+	if (command == 7)
+		optname = EBT_SO_GET_INIT_INFO;
+	else
+		optname = EBT_SO_GET_INFO;
+	if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen))
+		return -1;
+
+	if ( !(repl->entries = (char *) malloc(repl->entries_size)) )
+		print_memory();
+	if (repl->nentries) {
+		if (!(repl->counters = (struct ebt_counter *)
+		   malloc(repl->nentries * sizeof(struct ebt_counter))) )
+			print_memory();
+	}
+	else
+		repl->counters = NULL;
+
+	// we want to receive the counters
+	repl->num_counters = repl->nentries;
+	optlen += repl->entries_size + repl->num_counters *
+	   sizeof(struct ebt_counter);
+	if (command == 7)
+		optname = EBT_SO_GET_INIT_ENTRIES;
+	else
+		optname = EBT_SO_GET_ENTRIES;
+	if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen))
+		print_bug("hmm, what is wrong??? bug#1");
+
+	return 0;
+}
+
 // talk with kernel to receive the kernel's table
 int get_table(struct ebt_u_replace *u_repl)
 {
 	int i, j, k, hook;
-	socklen_t optlen;
 	struct ebt_replace repl;
 	struct ebt_u_entry **u_e;
 
-	get_sockfd();
-
-	optlen = sizeof(struct ebt_replace);
 	strcpy(repl.name, u_repl->name);
-	if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_INFO, &repl, &optlen))
-		return -1;
-
-	if ( !(repl.entries = (char *) malloc(repl.entries_size)) )
-		print_memory();
-	if (repl.nentries) {
-		if (!(repl.counters = (struct ebt_counter *)
-		   malloc(repl.nentries * sizeof(struct ebt_counter))) )
-			print_memory();
-	}
+	if (u_repl->filename != NULL)
+		retrieve_from_file(u_repl->filename, &repl, u_repl->command);
 	else
-		repl.counters = NULL;
-
-	// we want to receive the counters
-	repl.num_counters = repl.nentries;
-	optlen += repl.entries_size + repl.num_counters *
-	   sizeof(struct ebt_counter);
-	if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_ENTRIES, &repl, &optlen))
-		print_bug("hmm, what is wrong??? bug#1");
+		if (retrieve_from_kernel(&repl, u_repl->command) == -1)
+			return -1;
 
 	// translate the struct ebt_replace to a struct ebt_u_replace
 	memcpy(u_repl->name, repl.name, sizeof(u_repl->name));
diff --git a/ebtables.c b/ebtables.c
index b9d20a4..a8e564a 100644
--- a/ebtables.c
+++ b/ebtables.c
@@ -62,7 +62,8 @@
 };
 
 // default command line options
-static struct option ebt_original_options[] = {
+static struct option ebt_original_options[] =
+{
 	{ "append"        , required_argument, 0, 'A' },
 	{ "insert"        , required_argument, 0, 'I' },
 	{ "delete"        , required_argument, 0, 'D' },
@@ -94,13 +95,19 @@
 	{ "new-chain"     , required_argument, 0, 'N' },
 	{ "rename-chain"  , required_argument, 0, 'E' },
 	{ "delete-chain"  , required_argument, 0, 'X' },
+	{ "atomic-init"   , required_argument, 0, 7   },
+	// communication.c uses fact that atomic-commit equals 8
+	{ "atomic-commit" , required_argument, 0, 8   },
+	{ "atomic"        , required_argument, 0, 9   },
+	{ "atomic-save"   , required_argument, 0, 10  },
 	{ 0 }
 };
 
 static struct option *ebt_options = ebt_original_options;
 
 // yup, all the possible target names
-char* standard_targets[NUM_STANDARD_TARGETS] = {
+char* standard_targets[NUM_STANDARD_TARGETS] =
+{
 	"ACCEPT",
 	"DROP",
 	"CONTINUE",
@@ -172,7 +179,7 @@
 // Same holds for the struct ebt_match and struct ebt_watcher pointers
 struct ebt_u_entry *new_entry;
 
-void initialize_entry(struct ebt_u_entry *e)
+static void initialize_entry(struct ebt_u_entry *e)
 {
 	e->bitmask = EBT_NOPROTO;
 	e->invflags = 0;
@@ -191,7 +198,7 @@
 }
 
 // this doesn't free e, becoz the calling function might need e->next
-void free_u_entry(struct ebt_u_entry *e)
+static void free_u_entry(struct ebt_u_entry *e)
 {
 	struct ebt_u_match_list *m_l, *m_l2;
 	struct ebt_u_watcher_list *w_l, *w_l2;
@@ -406,7 +413,7 @@
 
 
 // used to parse /etc/ethertypes
-int disregard_whitespace(char *buffer, FILE *ifp)
+static int disregard_whitespace(char *buffer, FILE *ifp)
 {
 	int hlp;
 
@@ -419,7 +426,7 @@
 }
 
 // used to parse /etc/ethertypes
-int disregard_tabspace(char *buffer, FILE *ifp)
+static int disregard_tabspace(char *buffer, FILE *ifp)
 {
 	int hlp;
 
@@ -432,7 +439,7 @@
 }
 
 // helper function: processes a line of data from the file /etc/ethertypes
-int get_a_line(char *buffer, char *value, FILE *ifp)
+static int get_a_line(char *buffer, char *value, FILE *ifp)
 {
 	int i, hlp;
 	char anotherhlp;
@@ -729,7 +736,7 @@
 	struct ebt_u_entries *entries;
 };
 
-void check_for_loops()
+static void check_for_loops()
 {
 	int chain_nr , i, j , k, sp = 0, verdict;
 	struct ebt_u_entries *entries, *entries2;
@@ -832,7 +839,7 @@
 }
 
 // yup, print out help
-void print_help()
+static void print_help()
 {
 	struct ebt_u_match_list *m_l;
 	struct ebt_u_watcher_list *w_l;
@@ -857,6 +864,10 @@
 "--new-chain -N chain          : Create a user defined chain\n"
 "--rename-chain -E old new     : Rename a chain\n"
 "--delete-chain -X chain       : Delete a user defined chain\n"
+"--atomic-commit file          : update the kernel w/ the table contained in file\n"
+"--atomic-init file            : put the initial kernel table into file\n"
+"--atomic-save file            : put the current kernel table into file\n"
+"--atomic file                 : write changes to file instead of kernel\n"
 "Options:\n"
 "--proto  -p [!] proto         : protocol hexadecimal, by name or LENGTH\n"
 "--src    -s [!] address[/mask]: source mac address\n"
@@ -1327,7 +1338,7 @@
 }
 
 // execute command Z
-void zero_counters(int zerochain)
+static void zero_counters(int zerochain)
 {
 
 	if (zerochain == -1) {
@@ -1498,7 +1509,7 @@
 }
 
 // executes the final_check() function for all extensions used by the rule
-void do_final_checks(struct ebt_u_entry *e, struct ebt_u_entries *entries)
+static void do_final_checks(struct ebt_u_entry *e, struct ebt_u_entries *entries)
 {
 	struct ebt_u_match_list *m_l;
 	struct ebt_u_watcher_list *w_l;
@@ -1526,7 +1537,7 @@
 }
 
 // used for the -X command
-void check_for_references(int chain_nr)
+static void check_for_references(int chain_nr)
 {
 	int i = -1, j;
 	struct ebt_u_entries *entries;
@@ -1606,6 +1617,7 @@
 	replace.flags = 0;
 	replace.selected_hook = -1;
 	replace.command = 'h';
+	replace.filename = NULL;
 
 	new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
 	if (!new_entry)
@@ -1614,7 +1626,8 @@
 	initialize_entry(new_entry);
 
 	// The scenario induced by this loop makes that:
-	// '-t'  and '-M' (if specified) have to come before '-A' and the like
+	// '-t'  ,'-M' and --atomic (if specified) have to come
+	// before '-A' and the like
 
 	// getopt saves the day
 	while ((c = getopt_long(argc, argv,
@@ -2083,6 +2096,37 @@
 				print_error("--Lx not compatible with --Ln");
 			replace.flags |= LIST_X;
 			break;
+		case 8 : // atomic-commit
+			replace.command = c;
+			if (replace.flags & OPT_COMMAND)
+				print_error("Multiple commands not allowed");
+			replace.flags |= OPT_COMMAND;
+			replace.filename = (char *)malloc(strlen(optarg) + 1);
+			strcpy(replace.filename, optarg);
+			// get the information from the file
+			get_table(&replace);
+			replace.num_counters = 0;
+			free(replace.filename);
+			replace.filename = NULL;
+			break;
+		case 7 : // atomic-init
+		case 10: // atomic-save
+			replace.command = c;
+			if (replace.flags & OPT_COMMAND)
+				print_error("Multiple commands not allowed");
+			replace.flags |= OPT_COMMAND;
+			if ( !(table = find_table(replace.name)) )
+				print_error("Bad table name");
+			if (get_table(&replace)) {
+				ebtables_insmod("ebtables", modprobe);
+				if (get_table(&replace))
+					print_error("can't initialize ebtables "
+					"table %s", replace.name);
+			}
+		case 9 : // atomic
+			replace.filename = (char *)malloc(strlen(optarg) + 1);
+			strcpy(replace.filename, optarg);
+			break;
 
 		default:
 			// is it a target option?
@@ -2212,7 +2256,7 @@
 		}
 	} else if (replace.command == 'D')
 		delete_rule(rule_nr);
-	// commands -N, -E, -X fall through
+	// commands -N, -E, -X, --atomic-commit fall through
 
 	if (table->check)
 		table->check(&replace);