summaryrefslogtreecommitdiffstats
path: root/userspace/patches/incremental-patches/ebtables-v2.0pre10.001.diff
blob: 86f7e36656cff75b7fa1cdfdba348d5968880678 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
--- ebtables-v2.0pre9/Makefile	Sun Jul  7 16:29:50 2002
+++ ebtables-v2.0pre10.001/Makefile	Wed Jul 10 22:12:36 2002
@@ -2,7 +2,7 @@
 
 KERNEL_DIR?=/usr/src/linux
 PROGNAME:=ebtables
-PROGVERSION:="2.0pre9 (July 2002)"
+PROGVERSION:="2.0pre10 (July 2002)"
 
 MANDIR?=/usr/local/man
 CFLAGS:=-Wall -Wunused
@@ -51,9 +51,12 @@
 /etc/ethertypes: ethertypes
 	mkdir -p $(@D)
 	install -m 0644 -o root -g root $< $@
+.PHONY: exec
+exec: ebtables
+	install -m 0755 -o root -g root $< /sbin/ebtables
 
 install: $(MANDIR)/man8/ebtables.8 $(KERNEL_INCLUDES) \
-	ebtables /etc/ethertypes
+	ebtables /etc/ethertypes exec
 
 clean:
 	rm -f ebtables
--- ebtables-v2.0pre9/ebtables.c	Sat Jun 29 11:41:57 2002
+++ ebtables-v2.0pre10.001/ebtables.c	Mon Jul 15 22:14:59 2002
@@ -62,11 +62,17 @@
 };
 
 // default command line options
-static struct option ebt_original_options[] = {
+// do not mess around with the already assigned numbers unless
+// you know what you are doing
+static struct option ebt_original_options[] =
+{
 	{ "append"        , required_argument, 0, 'A' },
 	{ "insert"        , required_argument, 0, 'I' },
 	{ "delete"        , required_argument, 0, 'D' },
 	{ "list"          , optional_argument, 0, 'L' },
+	{ "Lc"            , no_argument      , 0, 4   },
+	{ "Ln"            , no_argument      , 0, 5   },
+	{ "Lx"            , no_argument      , 0, 6   },
 	{ "zero"          , optional_argument, 0, 'Z' },
 	{ "flush"         , optional_argument, 0, 'F' },
 	{ "policy"        , required_argument, 0, 'P' },
@@ -91,13 +97,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   },
+	{ "atomic-commit" , required_argument, 0, 8   },
+	{ "atomic"        , required_argument, 0, 9   },
+	{ "atomic-save"   , required_argument, 0, 10  },
+	{ "init-table"    , no_argument      , 0, 11  },
 	{ 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",
@@ -169,7 +181,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;
@@ -188,7 +200,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;
@@ -403,7 +415,7 @@
 
 
 // used to parse /etc/ethertypes
-int disregard_whitespace(char *buffer, FILE *ifp)
+static int disregard_whitespace(char *buffer, FILE *ifp)
 {
 	int hlp;
 
@@ -416,7 +428,7 @@
 }
 
 // used to parse /etc/ethertypes
-int disregard_tabspace(char *buffer, FILE *ifp)
+static int disregard_tabspace(char *buffer, FILE *ifp)
 {
 	int hlp;
 
@@ -429,7 +441,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;
@@ -507,6 +519,11 @@
 	}
 }
 
+// we use replace.flags, so we can't use the following values:
+// 0x01 == OPT_COMMAND, 0x02 == OPT_TABLE, 0x100 == OPT_ZERO
+#define LIST_N 0x04
+#define LIST_C 0x08
+#define LIST_X 0x10
 // helper function for list_rules()
 static void list_em(struct ebt_u_entries *entries)
 {
@@ -520,9 +537,14 @@
 	char name[21];
 
 	hlp = entries->entries;
-	printf("\nBridge chain: %s\nPolicy: %s\n", entries->name,
-	   standard_targets[-entries->policy - 1]);
-	printf("nr. of entries: %d \n", entries->nentries);
+	if (replace.flags & LIST_X && entries->policy != EBT_ACCEPT) {
+		printf("ebtables -t %s -P %s %s\n", replace.name,
+		   entries->name, standard_targets[-entries->policy - 1]);
+	} else if (!(replace.flags & LIST_X)) {
+		printf("\nBridge chain: %s\nPolicy: %s\n", entries->name,
+		   standard_targets[-entries->policy - 1]);
+		printf("nr. of entries: %d \n", entries->nentries);
+	}
 
 	i = entries->nentries;
 	while (i > 9) {
@@ -531,16 +553,21 @@
 	}
 
 	for (i = 0; i < entries->nentries; i++) {
-		digits = 0;
-		// A little work to get nice rule numbers.
-		j = i + 1;
-		while (j > 9) {
-			digits++;
-			j /= 10;
-		}
-		for (j = 0; j < space - digits; j++)
-			printf(" ");
-		printf("%d. ", i + 1);
+		if (replace.flags & LIST_N) {
+			digits = 0;
+			// A little work to get nice rule numbers.
+			j = i + 1;
+			while (j > 9) {
+				digits++;
+				j /= 10;
+			}
+			for (j = 0; j < space - digits; j++)
+				printf(" ");
+			printf("%d. ", i + 1);
+		}
+		if (replace.flags & LIST_X)
+			printf("ebtables -t %s -A %s ",
+			   replace.name, entries->name);
 
 		// Don't print anything about the protocol if no protocol was
 		// specified, obviously this means any protocol will do.
@@ -668,8 +695,9 @@
 		if (!t)
 			print_bug("Target not found");
 		t->print(hlp, hlp->t);
-		printf(", count = %llu",
-		   replace.counters[entries->counter_offset + i].pcnt);
+		if (replace.flags & LIST_C)
+			printf(", count = %llu",
+			   replace.counters[entries->counter_offset + i].pcnt);
 		printf("\n");
 		hlp = hlp->next;
 	}
@@ -710,7 +738,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;
@@ -813,7 +841,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;
@@ -833,11 +861,16 @@
 "--list   -L [chain]           : List the rules in a chain or in all chains\n"
 "--list   -L "DATABASEHOOKNAME"                : List the database (if present)\n"
 "--flush  -F [chain]           : Delete all rules in chain or in all chains\n"
+"--init-table                  : Replace the kernel table with the initial table\n"
 "--zero   -Z [chain]           : Put counters on zero in chain or in all chains\n"
 "--policy -P chain target      : Change policy on chain to target\n"
 "--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"
@@ -846,7 +879,7 @@
 "--out-if -o [!] name          : network output interface name\n"
 "--logical-in  [!] name        : logical bridge input interface name\n"
 "--logical-out [!] name        : logical bridge output interface name\n"
-"--modprobe -M                 : try to insert modules using this command\n"
+"--modprobe -M program         : try to insert modules using this program\n"
 "--version -V                  : print package version\n"
 "\n" ,
 	prog_name,
@@ -876,12 +909,28 @@
 {
 	int i;
 
-	printf("Bridge table: %s\n", table->name);
+	if (!(replace.flags & LIST_X))
+		printf("Bridge table: %s\n", table->name);
 	if (replace.selected_hook != -1) {
 		list_em(to_chain());
 	} else {
 		struct ebt_u_chain_list *cl = replace.udc;
 
+		// create new chains and rename standard chains when necessary
+		if (replace.flags & LIST_X) {
+			while (cl) {
+				printf("ebtables -t %s -N %s\n", replace.name,
+				   cl->udc->name);
+				cl = cl->next;
+			}
+			cl = replace.udc;
+			for (i = 0; i < NF_BR_NUMHOOKS; i++)
+				if (replace.valid_hooks & (1 << i) &&
+				   strcmp(replace.hook_entry[i]->name, hooknames[i]))
+					printf("ebtables -t %s -E %s %s\n",
+					   replace.name, hooknames[i],
+					   replace.hook_entry[i]->name);
+		}
 		i = 0;
 		while (1) {
 			if (i < NF_BR_NUMHOOKS) {
@@ -1292,7 +1341,7 @@
 }
 
 // execute command Z
-void zero_counters(int zerochain)
+static void zero_counters(int zerochain)
 {
 
 	if (zerochain == -1) {
@@ -1463,7 +1512,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;
@@ -1491,7 +1540,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;
@@ -1571,6 +1620,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)
@@ -1579,7 +1629,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,
@@ -2013,8 +2064,78 @@
 			allowbc = *optarg;
 			break;
 
-		default:
+		case 4  : // Lc
+			check_option(&replace.flags, LIST_C);
+			if (replace.selected_hook == DATABASEHOOKNR)
+				print_error("--Lc not valid for listing"
+				   " the database");
+			if (replace.command != 'L')
+				print_error("Use --Lc with -L");
+			if (replace.flags & LIST_X)
+				print_error("--Lx not compatible with --Lc");
+			replace.flags |= LIST_C;
+			break;
+		case 5  : // Ln
+			check_option(&replace.flags, LIST_N);
+			if (replace.selected_hook == DATABASEHOOKNR)
+				print_error("--Ln not valid for listing"
+				   " the database");
+			if (replace.command != 'L')
+				print_error("Use --Ln with -L");
+			if (replace.flags & LIST_X)
+				print_error("--Lx not compatible with --Ln");
+			replace.flags |= LIST_N;
+			break;
+		case 6  : // Lx
+			check_option(&replace.flags, LIST_X);
+			if (replace.selected_hook == DATABASEHOOKNR)
+				print_error("--Lx not valid for listing"
+				   " the database");
+			if (replace.command != 'L')
+				print_error("Use --Lx with -L");
+			if (replace.flags & LIST_C)
+				print_error("--Lx not compatible with --Lc");
+			if (replace.flags & LIST_N)
+				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
+		case 11: // init-table
+			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);
+			}
+			replace.num_counters = 0;
+			if (c == 11)
+				break;
+		case 9 : // atomic
+			replace.filename = (char *)malloc(strlen(optarg) + 1);
+			strcpy(replace.filename, optarg);
+			break;
 
+		default:
 			// is it a target option?
 			t = (struct ebt_u_target *)new_entry->t;
 			if ((t->parse(c - t->option_offset, argv, argc,
@@ -2142,7 +2263,8 @@
 		}
 	} else if (replace.command == 'D')
 		delete_rule(rule_nr);
-	// commands -N, -E, -X fall through
+	// commands -N, -E, -X, --atomic-commit, --atomic-commit, --atomic-save,
+	// --init-table fall through
 
 	if (table->check)
 		table->check(&replace);
--- ebtables-v2.0pre9/communication.c	Thu Jun 27 18:53:55 2002
+++ ebtables-v2.0pre10.001/communication.c	Mon Jul 15 22:35:14 2002
@@ -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("Couldn't open file %s", filename);
+	if (fwrite(data, sizeof(char), size, file) != size) {
+		fclose(file);
+		print_error("Couldn't 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,119 @@
 	return 0;
 }
 
-// talk with kernel to receive the kernel's table
-int get_table(struct ebt_u_replace *u_repl)
+static void retrieve_from_file(char *filename, struct ebt_replace *repl,
+   char command)
 {
-	int i, j, k, hook;
-	socklen_t optlen;
-	struct ebt_replace repl;
-	struct ebt_u_entry **u_e;
+	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);
+	}
+	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)) {
+		fclose(file);
+		print_error("File %s contains wrong table name or is corrupt",
+		   filename);
+	} else
+		if (!find_table(repl->name)) {
+			fclose(file);
+			print_error("File %s contains invalid table name",
+			   filename);
+		}
 
-	get_sockfd();
+	size = sizeof(struct ebt_replace) +
+	   repl->nentries * sizeof(struct ebt_counter) + repl->entries_size;
+	fseek(file, 0, SEEK_END);
+	if (size != ftell(file)) {
+		fclose(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)) {
+		fclose(file);
+		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);
-	strcpy(repl.name, u_repl->name);
-	if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_INFO, &repl, &optlen))
+	get_sockfd();
+	// --atomic-init || --init-table
+	if (command == 7 || command == 11)
+		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)) )
+	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))) )
+	if (repl->nentries) {
+		if (!(repl->counters = (struct ebt_counter *)
+		   malloc(repl->nentries * sizeof(struct ebt_counter))) )
 			print_memory();
 	}
 	else
-		repl.counters = NULL;
+		repl->counters = NULL;
 
 	// we want to receive the counters
-	repl.num_counters = repl.nentries;
-	optlen += repl.entries_size + repl.num_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))
+	if (command == 7 || command == 11)
+		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;
+	struct ebt_replace repl;
+	struct ebt_u_entry **u_e;
+
+	strcpy(repl.name, u_repl->name);
+	if (u_repl->filename != NULL)
+		retrieve_from_file(u_repl->filename, &repl, u_repl->command);
+	else
+		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));
--- ebtables-v2.0pre9/ChangeLog	Thu Jun 27 18:53:55 2002
+++ ebtables-v2.0pre10.001/ChangeLog	Sun Jul 14 21:30:18 2002
@@ -1,3 +1,8 @@
+20020714
+	* added --atomic options
+20020710
+	* some unlogged changes (due to lazyness)
+	* added --Lc, --Ln, --Lx
 20020625
 	* user defined chains support: added -N, -X, -E options.
 20020621
--- ebtables-v2.0pre9/ebtables.8	Thu Jun 27 18:53:55 2002
+++ ebtables-v2.0pre10.001/ebtables.8	Mon Jul 15 21:45:55 2002
@@ -37,6 +37,14 @@
 .br
 .BR "ebtables -[b] [" "y/n" "]"
 .br
+.BR "ebtables --init-table"
+.br
+.BR "ebtables --atomic-init " file
+.br
+.BR "ebtables --atomic-save " file
+.br
+.BR "ebtables --atomic-commit " file
+.br
 .SH DESCRIPTION
 .B ebtables
 is used to set up, maintain, and inspect the tables of Ethernet frame
@@ -148,11 +156,34 @@
 database is independent from the rest of
 .B ebtables
 and is in a different kernel module.
+.br
+The following three options change the output when not listing the
+database:
+.br
+.B "--Ln"
+.br
+Puts rule numbers in front of every rule.
+.br
+.B "--Lc"
+.br
+Puts the counter value at the end of every rule.
+.br
+.B "--Lx"
+.br
+The output is directly usable as executable commands in a script, to be
+run f.e. at bootup. This option is incompatible with the previous two
+options. When no chain name was specified for the
+.B "-L"
+command, all necessary commands for making the user defined chains and
+renaming the standard chains will be made.
 .TP
 .B "-F, --flush"
 Flush the selected chain. If no chain is selected, every chain will be
 flushed. This does not change the policy of the chain.
 .TP
+.B "--init-table"
+Replace the current table data by the initial table data.
+.TP
 .B "-Z, --zero"
 Put the counters of the selected chain on zero. If no chain is selected, all the counters
 are put on zero. This can be used in conjunction with the -L command (see above). 
@@ -178,6 +209,30 @@
 structure of the table. It is also allowed to rename a base chain, f.e.
 if you like PREBRIDGING more than PREROUTING. Be sure to talk about the
 standard chain names when you would ask a question on a mailing list.
+.TP
+.B "--atomic-init"
+Copy the kernel's initial data of the table to the specified
+file. This can be used as the first action, after which rules are added
+to the file.
+.TP
+.B "--atomic-save"
+Copy the kernel's current data of the table to the specified
+file. This can be used as the first action, after which rules are added
+to the file.
+.TP
+.B "--atomic-commit"
+Replace the kernel table data with the data contained in the specified
+file. This is a useful command that allows you to put all your rules of a
+certain table into the kernel at once, saving the kernel a lot of precious
+time. The file which contains the table data is constructed by using
+either the
+.B "--atomic-init"
+or the
+.B "--atomic-save"
+command to get a starting file. After that, using the
+.B "--atomic"
+option when constructing rules allows you to extend the file and build up
+the complete wanted table.
 .SS
 PARAMETERS
 The following parameters make up a rule specification (as used in the add
@@ -280,8 +335,8 @@
 .B ebtables
 will try to write help about those extensions. E.g. ebtables -h snat log ip arp.
 .TP
-.BR "-b --db " "[\fIy/n\fP]"
-.IR "" "Enable (" y ") or disable (" n ") the database."
+.BR "-b --db [" "y/n" "]"
+Enable (y) or disable (n) the database.
 .TP
 .BR "-j, --jump " "\fItarget\fP"
 The target of the rule. This is one of the following values:
@@ -291,9 +346,15 @@
 or a target extension, see
 .BR "TARGET EXTENSIONS" .
 .TP
-.BR "-M, --modprobe " "\fIcommand\fP"
-When talking to the kernel, use this
-.IR command " to try to automatically load missing kernel modules."
+.BR "--atomic " file
+Let the command operate on the specified file. The data of the table to
+operate on will be extracted from the file and the result of the operation
+will be saved back into the file. If specified, this option should come
+before the command specification.
+.TP
+.BR "-M, --modprobe " "program"
+When talking to the kernel, use this program to try to automatically load
+missing kernel modules.
 .SH MATCH EXTENSIONS
 .B ebtables
 extensions are precompiled into the userspace tool. So there is no need
--- ebtables-v2.0pre9/include/ebtables_u.h	Thu Jun 27 18:53:55 2002
+++ ebtables-v2.0pre10.001/include/ebtables_u.h	Sat Jul 13 21:13:46 2002
@@ -66,6 +66,8 @@
 	char command;
 	// here we stick the hook to do our thing on (can be -1 if unspecified)
 	int selected_hook;
+	// used for the atomic option
+	char *filename;
 };
 
 struct ebt_u_table
@@ -191,6 +193,7 @@
 struct ebt_u_target *find_target(const char *name);
 struct ebt_u_match *find_match(const char *name);
 struct ebt_u_watcher *find_watcher(const char *name);
+struct ebt_u_table *find_table(char *name);
 void deliver_counters(struct ebt_u_replace *repl,
    unsigned short * counterchanges);
 void deliver_table(struct ebt_u_replace *repl);