blob: 6683611c7d63932b4dc4f4d86ef96a44bca2eed6 [file] [log] [blame]
Bart De Schuymer6622a012005-01-19 21:09:05 +00001/*
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}
43
44int main(int argc_, char *argv_[])
45{
46 char *argv[EBTD_ARGC_MAX], *args[4], name[] = "mkdir",
47 mkdir_option[] = "-p", mkdir_dir[] = EBTD_PIPE_DIR,
48 cmdline[EBTD_CMDLINE_MAXLN];
49 int readfd, stop = 0, base = 0, offset = 0, n = 0, ret = 0;
50
51 /* Make sure the pipe directory exists */
52 args[0] = name;
53 args[1] = mkdir_option;
54 args[2] = mkdir_dir;
55 args[3] = NULL;
56 switch (fork()) {
57 case 0:
58 execvp(args[0], args);
59
60 /* Not usually reached */
61 exit(0);
62 case -1:
63 return -1;
64
65 default: /* Parent */
66 wait(NULL);
67 }
68
69 if (mkfifo(EBTD_PIPE, 0600) < 0 && errno != EEXIST) {
70 printf("Error creating FIFO " EBTD_PIPE "\n");
71 ret = -1;
72 goto do_exit;
73 }
74
75 if ((readfd = open(EBTD_PIPE, O_RDONLY | O_NONBLOCK, 0)) == -1) {
76 perror("open");
77 ret = -1;
78 goto do_exit;
79 }
80
81 if (signal(SIGPIPE, sigpipe_handler) == SIG_ERR) {
82 perror("signal");
83 ret = -1;
84 goto do_exit;
85 }
86
87 ebt_silent = 1;
88
89 strcpy(replace[0].name, "filter");
90 strcpy(replace[1].name, "nat");
91 strcpy(replace[2].name, "broute");
92
93 ebt_early_init_once();
94
95 while (!stop) {
96 int n2, i, argc, table_nr, ntot;
97
98 /* base == 0 */
99 ntot = read(readfd, cmdline+offset, EBTD_CMDLINE_MAXLN-offset-1);
100 if (ntot <= 0)
101 continue;
102 ntot += offset;
103continue_read:
104 /* Change all ' ' into '\0'. This implies that the user is not
105 * allowed to use spaces (that don't distinguish options or
106 * commands) in her rules. This comes down to not allowing spaces
107 * in options like the string of --ulog-prefix (use '_' instead).
108 */
109 for (; offset < ntot; n++, offset++) {
110 if (cmdline[offset] == ' ')
111 cmdline[offset] = '\0';
112 if (cmdline[offset] == '\n') {
113 cmdline[offset] = '\0';
114 break;
115 }
116 }
117 if (n == 0) {
118 if (offset == ntot) {
119 /* The ntot bytes were parsed and ended with '\n' */
120 base = 0;
121 offset = 0;
122 continue;
123 }
124 offset++;
125 base = offset;
126 n = 0;
127 goto continue_read;
128 }
129 if (offset == ntot) { /* The ntot bytes were parsed but no complete rule is yet specified */
130 if (base == 0) {
131 ebt_print_error("ebtablesd: the maximum command line length is %d", EBTD_CMDLINE_MAXLN-1);
132 goto write_msg;
133 }
134 memmove(cmdline, cmdline+base+offset, ntot-offset);
135 offset -= base;
136 offset++;
137 base = 0;
138 continue;
139 }
140
141 table_nr = 0;
142 n2 = 0;
143 argc = 0;
144 while (n2 < n && argc < EBTD_ARGC_MAX) {
145 argv[argc++] = cmdline + base + n2;
146 n2 += strlen(cmdline + base + n2) + 1;
147 }
148 offset++; /* Move past the '\n' */
149 base = offset;
150
151 if (argc > EBTD_ARGC_MAX) {
152 ebt_print_error("ebtablesd: maximum %d arguments "
153 "allowed", EBTD_ARGC_MAX - 1);
154 goto write_msg;
155 }
156 if (argc == 1) {
157 ebt_print_error("ebtablesd: no arguments");
158 goto write_msg;
159 }
160
161 /* Parse the options */
162 if (!strcmp(argv[1], "-t")) {
163 if (argc < 3) {
164 ebt_print_error("ebtablesd: -t but no table");
165 goto write_msg;
166 }
167 for (i = 0; i < 3; i++)
168 if (!strcmp(replace[i].name, argv[2]))
169 break;
170 if (i == 3) {
171 ebt_print_error("ebtablesd: table '%s' was "
172 "not recognized", argv[2]);
173 goto write_msg;
174 }
175 table_nr = i;
176 } else if (!strcmp(argv[1], "free")) {
177 if (argc != 3) {
178 ebt_print_error("ebtablesd: command free "
179 "needs exactly one argument");
180 goto write_msg;
181 }
182 for (i = 0; i < 3; i++)
183 if (!strcmp(replace[i].name, argv[2]))
184 break;
185 if (i == 3) {
186 ebt_print_error("ebtablesd: table '%s' was "
187 "not recognized", argv[2]);
188 goto write_msg;
189 }
190 if (!(replace[i].flags & OPT_KERNELDATA)) {
191 ebt_print_error("ebtablesd: table %s has not "
192 "been opened");
193 goto write_msg;
194 }
195 ebt_cleanup_replace(&replace[i]);
196 replace[i].flags &= ~OPT_KERNELDATA;
197 goto write_msg;
198 } else if (!strcmp(argv[1], "open")) {
199 if (argc != 3) {
200 ebt_print_error("ebtablesd: command open "
201 "needs exactly one argument");
202 goto write_msg;
203 }
204
205 for (i = 0; i < 3; i++)
206 if (!strcmp(replace[i].name, argv[2]))
207 break;
208 if (i == 3) {
209 ebt_print_error("ebtablesd: table '%s' was "
210 "not recognized", argv[2]);
211 goto write_msg;
212 }
213 if (replace[i].flags & OPT_KERNELDATA) {
214 ebt_print_error("ebtablesd: table %s needs to "
215 "be freed before it can be "
216 "opened");
217 goto write_msg;
218 }
219 if (!ebt_get_kernel_table(&replace[i], 0)) {
220 replace[i].flags |= OPT_KERNELDATA;
221 open_method[i] = OPEN_METHOD_KERNEL;
222 }
223 goto write_msg;
224 } else if (!strcmp(argv[1], "fopen")) {
225 struct ebt_u_replace tmp;
226
227 memset(&tmp, 0, sizeof(tmp));
228 if (argc != 4) {
229 ebt_print_error("ebtablesd: command fopen "
230 "needs exactly two arguments");
231 goto write_msg;
232 }
233
234 for (i = 0; i < 3; i++)
235 if (!strcmp(replace[i].name, argv[2]))
236 break;
237 if (i == 3) {
238 ebt_print_error("ebtablesd: table '%s' was "
239 "not recognized", argv[2]);
240 goto write_msg;
241 }
242 if (replace[i].flags & OPT_KERNELDATA) {
243 ebt_print_error("ebtablesd: table %s needs to "
244 "be freed before it can be "
245 "opened");
246 goto write_msg;
247 }
248 tmp.filename = (char *)malloc(strlen(argv[3]) + 1);
249 if (!tmp.filename) {
250 ebt_print_error("Out of memory");
251 goto write_msg;
252 }
253 strcpy(tmp.filename, argv[3]);
254 strcpy(tmp.name, "filter");
255 tmp.command = 'L'; /* Make sure retrieve_from_file()
256 * doesn't complain about wrong
257 * table name */
258
259 ebt_get_kernel_table(&tmp, 0);
260 free(tmp.filename);
261 tmp.filename = NULL;
262 if (ebt_errormsg[0] != '\0')
263 goto write_msg;
264
265 if (strcmp(tmp.name, argv[2])) {
266 ebt_print_error("ebtablesd: opened file with "
267 "wrong table name '%s'", tmp.name);
268 ebt_cleanup_replace(&tmp);
269 goto write_msg;
270 }
271 replace[i] = tmp;
272 replace[i].command = '\0';
273 replace[i].flags |= OPT_KERNELDATA;
274 open_method[i] = OPEN_METHOD_FILE;
275 goto write_msg;
276 } else if (!strcmp(argv[1], "commit")) {
277 if (argc != 3) {
278 ebt_print_error("ebtablesd: command commit "
279 "needs exactly one argument");
280 goto write_msg;
281 }
282
283 for (i = 0; i < 3; i++)
284 if (!strcmp(replace[i].name, argv[2]))
285 break;
286 if (i == 3) {
287 ebt_print_error("ebtablesd: table '%s' was "
288 "not recognized", argv[2]);
289 goto write_msg;
290 }
291 if (!(replace[i].flags & OPT_KERNELDATA)) {
292 ebt_print_error("ebtablesd: table %s has not "
293 "been opened");
294 goto write_msg;
295 }
296 /* The counters from the kernel are useless if we
297 * didn't start from a kernel table */
298 if (open_method[i] == OPEN_METHOD_FILE)
299 replace[i].num_counters = 0;
300 ebt_deliver_table(&replace[i]);
301 if (ebt_errormsg[0] == '\0' && open_method[i] == OPEN_METHOD_KERNEL)
302 ebt_deliver_counters(&replace[i], EXEC_STYLE_DAEMON);
303 goto write_msg;
304 } else if (!strcmp(argv[1], "fcommit")) {
305 if (argc != 4) {
306 ebt_print_error("ebtablesd: command commit "
307 "needs exactly two argument");
308 goto write_msg;
309 }
310
311 for (i = 0; i < 3; i++)
312 if (!strcmp(replace[i].name, argv[2]))
313 break;
314 if (i == 3) {
315 ebt_print_error("ebtablesd: table '%s' was "
316 "not recognized", argv[2]);
317 goto write_msg;
318 }
319 if (!(replace[i].flags & OPT_KERNELDATA)) {
320 ebt_print_error("ebtablesd: table %s has not "
321 "been opened");
322 goto write_msg;
323 }
324 replace[i].filename = (char *)malloc(strlen(argv[3]) + 1);
325 if (!replace[i].filename) {
326 ebt_print_error("Out of memory");
327 goto write_msg;
328 }
329 strcpy(replace[i].filename, argv[3]);
330 ebt_deliver_table(&replace[i]);
331 if (ebt_errormsg[0] == '\0' && open_method[i] == OPEN_METHOD_KERNEL)
332 ebt_deliver_counters(&replace[i], EXEC_STYLE_DAEMON);
333 free(replace[i].filename);
334 replace[i].filename = NULL;
335 goto write_msg;
336 }else if (!strcmp(argv[1], "quit")) {
337 if (argc != 2) {
338 ebt_print_error("ebtablesd: command quit does "
339 "not take any arguments");
340 goto write_msg;
341 }
342 stop = 1;
343 goto write_msg;
344 }
345 if (!(replace[table_nr].flags & OPT_KERNELDATA)) {
346 ebt_print_error("ebtablesd: table %s has not been "
347 "opened", replace[table_nr].name);
348 goto write_msg;
349 }
Bart De Schuymerb92edb82005-01-23 19:08:14 +0000350 optind = 0; /* Setting optind = 1 causes serious annoyances */
Bart De Schuymer6622a012005-01-19 21:09:05 +0000351 do_command(argc, argv, EXEC_STYLE_DAEMON, &replace[table_nr]);
352 ebt_reinit_extensions();
353write_msg:
354#ifndef SILENT_DAEMON
355 if (ebt_errormsg[0] != '\0')
356 printf("%s\n", ebt_errormsg);
357#endif
358 ebt_errormsg[0]= '\0';
359 n = 0;
360 goto continue_read;
361 }
362do_exit:
363 unlink(EBTD_PIPE);
364
365 return 0;
366}