1/*
2 * ebtablesd.c, January 2005
3 *
4 * Author: Bart De Schuymer
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
24#include <signal.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <sys/wait.h>
28#include <fcntl.h>
29#include <errno.h>
30#include "include/ebtables_u.h"
31
32#define OPT_KERNELDATA	0x800 /* Also defined in ebtables.c */
33
34static struct ebt_u_replace replace[3];
35#define OPEN_METHOD_FILE 1
36#define OPEN_METHOD_KERNEL 2
37static int open_method[3];
38void ebt_early_init_once();
39
40static void sigpipe_handler(int sig)
41{
42}
43static void copy_table_names()
44{
45	strcpy(replace[0].name, "filter");
46	strcpy(replace[1].name, "nat");
47	strcpy(replace[2].name, "broute");
48}
49
50int main(int argc_, char *argv_[])
51{
52	char *argv[EBTD_ARGC_MAX], *args[4], name[] = "mkdir",
53	     mkdir_option[] = "-p", mkdir_dir[] = EBTD_PIPE_DIR,
54	     cmdline[EBTD_CMDLINE_MAXLN];
55	int readfd, base = 0, offset = 0, n = 0, ret = 0, quotemode = 0;
56
57	/* Make sure the pipe directory exists */
58	args[0] = name;
59	args[1] = mkdir_option;
60	args[2] = mkdir_dir;
61	args[3] = NULL;
62	switch (fork()) {
63	case 0:
64		execvp(args[0], args);
65
66		/* Not usually reached */
67		exit(0);
68	case -1:
69		return -1;
70
71	default: /* Parent */
72		wait(NULL);
73	}
74
75	if (mkfifo(EBTD_PIPE, 0600) < 0 && errno != EEXIST) {
76		printf("Error creating FIFO " EBTD_PIPE "\n");
77		ret = -1;
78		goto do_exit;
79	}
80
81	if ((readfd = open(EBTD_PIPE, O_RDONLY | O_NONBLOCK, 0)) == -1) {
82		perror("open");
83		ret = -1;
84		goto do_exit;
85	}
86
87	if (signal(SIGPIPE, sigpipe_handler) == SIG_ERR) {
88		perror("signal");
89		ret = -1;
90		goto do_exit;
91	}
92
93	ebt_silent = 1;
94
95	copy_table_names();
96	ebt_early_init_once();
97
98	while (1) {
99		int n2, i, argc, table_nr, ntot;
100
101		/* base == 0 */
102		ntot = read(readfd, cmdline+offset, EBTD_CMDLINE_MAXLN-offset-1);
103		if (ntot <= 0)
104			continue;
105		ntot += offset;
106continue_read:
107		/* Put '\0' between arguments. */
108		for (; offset < ntot; n++, offset++) {
109			if (cmdline[offset] == '\"') {
110				quotemode ^= 1;
111				cmdline[offset] = '\0';
112			} else if (!quotemode && cmdline[offset] == ' ') {
113				cmdline[offset] = '\0';
114			} else if (cmdline[offset] == '\n') {
115				if (quotemode)
116					ebt_print_error("ebtablesd: wrong number of \" delimiters");
117				cmdline[offset] = '\0';
118				break;
119			}
120		}
121		if (n == 0) {
122			if (offset == ntot) {
123				/* The ntot bytes were parsed and ended with '\n' */
124				base = 0;
125				offset = 0;
126				continue;
127			}
128			offset++;
129			base = offset;
130			n = 0;
131			goto continue_read;
132		}
133		if (offset == ntot) { /* The ntot bytes were parsed but no complete rule is yet specified */
134			if (base == 0) {
135				ebt_print_error("ebtablesd: the maximum command line length is %d", EBTD_CMDLINE_MAXLN-1);
136				goto write_msg;
137			}
138			memmove(cmdline, cmdline+base+offset, ntot-offset);
139			offset -= base;
140			offset++;
141			base = 0;
142			continue;
143		}
144
145		table_nr = 0;
146		n2 = 0;
147		argc = 0;
148		while (n2 < n && argc < EBTD_ARGC_MAX) {
149			if (*(cmdline + base + n2) == '\0') {
150				n2++;
151				continue;
152			}
153			argv[argc++] = cmdline + base + n2;
154			n2 += strlen(cmdline + base + n2) + 1;
155		}
156		offset++; /* Move past the '\n' */
157		base = offset;
158
159		if (argc > EBTD_ARGC_MAX) {
160			ebt_print_error("ebtablesd: maximum %d arguments "
161			                "allowed", EBTD_ARGC_MAX - 1);
162			goto write_msg;
163		}
164		if (argc == 1) {
165			ebt_print_error("ebtablesd: no arguments");
166			goto write_msg;
167		}
168
169		/* Parse the options */
170		if (!strcmp(argv[1], "-t")) {
171			if (argc < 3) {
172				ebt_print_error("ebtablesd: -t but no table");
173				goto write_msg;
174			}
175			for (i = 0; i < 3; i++)
176				if (!strcmp(replace[i].name, argv[2]))
177					break;
178			if (i == 3) {
179				ebt_print_error("ebtablesd: table '%s' was "
180				                "not recognized", argv[2]);
181				goto write_msg;
182			}
183			table_nr = i;
184		} else if (!strcmp(argv[1], "free")) {
185			if (argc != 3) {
186				ebt_print_error("ebtablesd: command free "
187				                "needs exactly one argument");
188				goto write_msg;
189			}
190			for (i = 0; i < 3; i++)
191				if (!strcmp(replace[i].name, argv[2]))
192					break;
193			if (i == 3) {
194				ebt_print_error("ebtablesd: table '%s' was "
195				                "not recognized", argv[2]);
196				goto write_msg;
197			}
198			if (!(replace[i].flags & OPT_KERNELDATA)) {
199				ebt_print_error("ebtablesd: table %s has not "
200				                "been opened");
201				goto write_msg;
202			}
203			ebt_cleanup_replace(&replace[i]);
204			copy_table_names();
205			replace[i].flags &= ~OPT_KERNELDATA;
206			goto write_msg;
207		} else if (!strcmp(argv[1], "open")) {
208			if (argc != 3) {
209				ebt_print_error("ebtablesd: command open "
210				                "needs exactly one argument");
211				goto write_msg;
212			}
213
214			for (i = 0; i < 3; i++)
215				if (!strcmp(replace[i].name, argv[2]))
216					break;
217			if (i == 3) {
218				ebt_print_error("ebtablesd: table '%s' was "
219				                "not recognized", argv[2]);
220				goto write_msg;
221			}
222			if (replace[i].flags & OPT_KERNELDATA) {
223				ebt_print_error("ebtablesd: table %s needs to "
224				                "be freed before it can be "
225				                "opened");
226				goto write_msg;
227			}
228			if (!ebt_get_kernel_table(&replace[i], 0)) {
229				replace[i].flags |= OPT_KERNELDATA;
230				open_method[i] = OPEN_METHOD_KERNEL;
231			}
232			goto write_msg;
233		} else if (!strcmp(argv[1], "fopen")) {
234			struct ebt_u_replace tmp;
235
236			memset(&tmp, 0, sizeof(tmp));
237			if (argc != 4) {
238				ebt_print_error("ebtablesd: command fopen "
239				                "needs exactly two arguments");
240				goto write_msg;
241			}
242
243			for (i = 0; i < 3; i++)
244				if (!strcmp(replace[i].name, argv[2]))
245					break;
246			if (i == 3) {
247				ebt_print_error("ebtablesd: table '%s' was "
248				                "not recognized", argv[2]);
249				goto write_msg;
250			}
251			if (replace[i].flags & OPT_KERNELDATA) {
252				ebt_print_error("ebtablesd: table %s needs to "
253				                "be freed before it can be "
254				                "opened");
255				goto write_msg;
256			}
257			tmp.filename = (char *)malloc(strlen(argv[3]) + 1);
258			if (!tmp.filename) {
259				ebt_print_error("Out of memory");
260				goto write_msg;
261			}
262			strcpy(tmp.filename, argv[3]);
263			strcpy(tmp.name, "filter");
264			tmp.command = 'L'; /* Make sure retrieve_from_file()
265			                    * doesn't complain about wrong
266			                    * table name */
267
268			ebt_get_kernel_table(&tmp, 0);
269			free(tmp.filename);
270			tmp.filename = NULL;
271			if (ebt_errormsg[0] != '\0')
272				goto write_msg;
273
274			if (strcmp(tmp.name, argv[2])) {
275				ebt_print_error("ebtablesd: opened file with "
276				                "wrong table name '%s'", tmp.name);
277				ebt_cleanup_replace(&tmp);
278				goto write_msg;
279			}
280			replace[i] = tmp;
281			replace[i].command = '\0';
282			replace[i].flags |= OPT_KERNELDATA;
283			open_method[i] = OPEN_METHOD_FILE;
284			goto write_msg;
285		} else if (!strcmp(argv[1], "commit")) {
286			if (argc != 3) {
287				ebt_print_error("ebtablesd: command commit "
288				                "needs exactly one argument");
289				goto write_msg;
290			}
291
292			for (i = 0; i < 3; i++)
293				if (!strcmp(replace[i].name, argv[2]))
294					break;
295			if (i == 3) {
296				ebt_print_error("ebtablesd: table '%s' was "
297				                "not recognized", argv[2]);
298				goto write_msg;
299			}
300			if (!(replace[i].flags & OPT_KERNELDATA)) {
301				ebt_print_error("ebtablesd: table %s has not "
302				                "been opened");
303				goto write_msg;
304			}
305			/* The counters from the kernel are useless if we
306			 * didn't start from a kernel table */
307			if (open_method[i] == OPEN_METHOD_FILE)
308				replace[i].num_counters = 0;
309			ebt_deliver_table(&replace[i]);
310			if (ebt_errormsg[0] == '\0' && open_method[i] == OPEN_METHOD_KERNEL)
311				ebt_deliver_counters(&replace[i]);
312			goto write_msg;
313		} else if (!strcmp(argv[1], "fcommit")) {
314			if (argc != 4) {
315				ebt_print_error("ebtablesd: command commit "
316				                "needs exactly two argument");
317				goto write_msg;
318			}
319
320			for (i = 0; i < 3; i++)
321				if (!strcmp(replace[i].name, argv[2]))
322					break;
323			if (i == 3) {
324				ebt_print_error("ebtablesd: table '%s' was "
325				                "not recognized", argv[2]);
326				goto write_msg;
327			}
328			if (!(replace[i].flags & OPT_KERNELDATA)) {
329				ebt_print_error("ebtablesd: table %s has not "
330				                "been opened");
331				goto write_msg;
332			}
333			replace[i].filename = (char *)malloc(strlen(argv[3]) + 1);
334			if (!replace[i].filename) {
335				ebt_print_error("Out of memory");
336				goto write_msg;
337			}
338			strcpy(replace[i].filename, argv[3]);
339			ebt_deliver_table(&replace[i]);
340			if (ebt_errormsg[0] == '\0' && open_method[i] == OPEN_METHOD_KERNEL)
341				ebt_deliver_counters(&replace[i]);
342			free(replace[i].filename);
343			replace[i].filename = NULL;
344			goto write_msg;
345		}else if (!strcmp(argv[1], "quit")) {
346			if (argc != 2) {
347				ebt_print_error("ebtablesd: command quit does "
348				                "not take any arguments");
349				goto write_msg;
350			}
351			break;
352		}
353		if (!(replace[table_nr].flags & OPT_KERNELDATA)) {
354			ebt_print_error("ebtablesd: table %s has not been "
355			                "opened", replace[table_nr].name);
356			goto write_msg;
357		}
358		optind = 0; /* Setting optind = 1 causes serious annoyances */
359		do_command(argc, argv, EXEC_STYLE_DAEMON, &replace[table_nr]);
360		ebt_reinit_extensions();
361write_msg:
362#ifndef SILENT_DAEMON
363		if (ebt_errormsg[0] != '\0')
364			printf("%s.\n", ebt_errormsg);
365#endif
366		ebt_errormsg[0]= '\0';
367		n = 0;
368		goto continue_read;
369	}
370do_exit:
371	unlink(EBTD_PIPE);
372
373	return 0;
374}
375