| /* |
| * ebtablesd.c, January 2005 |
| * |
| * Author: Bart De Schuymer |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of the |
| * License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <signal.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/wait.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include "include/ebtables_u.h" |
| |
| #define OPT_KERNELDATA 0x800 /* Also defined in ebtables.c */ |
| |
| static struct ebt_u_replace replace[3]; |
| #define OPEN_METHOD_FILE 1 |
| #define OPEN_METHOD_KERNEL 2 |
| static int open_method[3]; |
| void ebt_early_init_once(); |
| |
| static void sigpipe_handler(int sig) |
| { |
| } |
| static void copy_table_names() |
| { |
| strcpy(replace[0].name, "filter"); |
| strcpy(replace[1].name, "nat"); |
| strcpy(replace[2].name, "broute"); |
| } |
| |
| int main(int argc_, char *argv_[]) |
| { |
| char *argv[EBTD_ARGC_MAX], *args[4], name[] = "mkdir", |
| mkdir_option[] = "-p", mkdir_dir[] = EBTD_PIPE_DIR, |
| cmdline[EBTD_CMDLINE_MAXLN]; |
| int readfd, base = 0, offset = 0, n = 0, quotemode = 0; |
| |
| /* Make sure the pipe directory exists */ |
| args[0] = name; |
| args[1] = mkdir_option; |
| args[2] = mkdir_dir; |
| args[3] = NULL; |
| switch (fork()) { |
| case 0: |
| execvp(args[0], args); |
| |
| /* Not usually reached */ |
| exit(0); |
| case -1: |
| return -1; |
| |
| default: /* Parent */ |
| wait(NULL); |
| } |
| |
| if (mkfifo(EBTD_PIPE, 0600) < 0 && errno != EEXIST) { |
| printf("Error creating FIFO " EBTD_PIPE "\n"); |
| goto do_exit; |
| } |
| |
| if ((readfd = open(EBTD_PIPE, O_RDONLY | O_NONBLOCK, 0)) == -1) { |
| perror("open"); |
| goto do_exit; |
| } |
| |
| if (signal(SIGPIPE, sigpipe_handler) == SIG_ERR) { |
| perror("signal"); |
| goto do_exit; |
| } |
| |
| ebt_silent = 1; |
| |
| copy_table_names(); |
| ebt_early_init_once(); |
| |
| while (1) { |
| int n2, i, argc, table_nr, ntot; |
| |
| /* base == 0 */ |
| ntot = read(readfd, cmdline+offset, EBTD_CMDLINE_MAXLN-offset-1); |
| if (ntot <= 0) |
| continue; |
| ntot += offset; |
| continue_read: |
| /* Put '\0' between arguments. */ |
| for (; offset < ntot; n++, offset++) { |
| if (cmdline[offset] == '\"') { |
| quotemode ^= 1; |
| cmdline[offset] = '\0'; |
| } else if (!quotemode && cmdline[offset] == ' ') { |
| cmdline[offset] = '\0'; |
| } else if (cmdline[offset] == '\n') { |
| if (quotemode) |
| ebt_print_error("ebtablesd: wrong number of \" delimiters"); |
| cmdline[offset] = '\0'; |
| break; |
| } |
| } |
| if (n == 0) { |
| if (offset == ntot) { |
| /* The ntot bytes were parsed and ended with '\n' */ |
| base = 0; |
| offset = 0; |
| continue; |
| } |
| offset++; |
| base = offset; |
| n = 0; |
| goto continue_read; |
| } |
| if (offset == ntot) { /* The ntot bytes were parsed but no complete rule is yet specified */ |
| if (base == 0) { |
| ebt_print_error("ebtablesd: the maximum command line length is %d", EBTD_CMDLINE_MAXLN-1); |
| goto write_msg; |
| } |
| memmove(cmdline, cmdline+base+offset, ntot-offset); |
| offset -= base; |
| offset++; |
| base = 0; |
| continue; |
| } |
| |
| table_nr = 0; |
| n2 = 0; |
| argc = 0; |
| while (n2 < n && argc < EBTD_ARGC_MAX) { |
| if (*(cmdline + base + n2) == '\0') { |
| n2++; |
| continue; |
| } |
| argv[argc++] = cmdline + base + n2; |
| n2 += strlen(cmdline + base + n2) + 1; |
| } |
| offset++; /* Move past the '\n' */ |
| base = offset; |
| |
| if (argc > EBTD_ARGC_MAX) { |
| ebt_print_error("ebtablesd: maximum %d arguments " |
| "allowed", EBTD_ARGC_MAX - 1); |
| goto write_msg; |
| } |
| if (argc == 1) { |
| ebt_print_error("ebtablesd: no arguments"); |
| goto write_msg; |
| } |
| |
| /* Parse the options */ |
| if (!strcmp(argv[1], "-t")) { |
| if (argc < 3) { |
| ebt_print_error("ebtablesd: -t but no table"); |
| goto write_msg; |
| } |
| for (i = 0; i < 3; i++) |
| if (!strcmp(replace[i].name, argv[2])) |
| break; |
| if (i == 3) { |
| ebt_print_error("ebtablesd: table '%s' was " |
| "not recognized", argv[2]); |
| goto write_msg; |
| } |
| table_nr = i; |
| } else if (!strcmp(argv[1], "free")) { |
| if (argc != 3) { |
| ebt_print_error("ebtablesd: command free " |
| "needs exactly one argument"); |
| goto write_msg; |
| } |
| for (i = 0; i < 3; i++) |
| if (!strcmp(replace[i].name, argv[2])) |
| break; |
| if (i == 3) { |
| ebt_print_error("ebtablesd: table '%s' was " |
| "not recognized", argv[2]); |
| goto write_msg; |
| } |
| if (!(replace[i].flags & OPT_KERNELDATA)) { |
| ebt_print_error("ebtablesd: table %s has not " |
| "been opened"); |
| goto write_msg; |
| } |
| ebt_cleanup_replace(&replace[i]); |
| copy_table_names(); |
| replace[i].flags &= ~OPT_KERNELDATA; |
| goto write_msg; |
| } else if (!strcmp(argv[1], "open")) { |
| if (argc != 3) { |
| ebt_print_error("ebtablesd: command open " |
| "needs exactly one argument"); |
| goto write_msg; |
| } |
| |
| for (i = 0; i < 3; i++) |
| if (!strcmp(replace[i].name, argv[2])) |
| break; |
| if (i == 3) { |
| ebt_print_error("ebtablesd: table '%s' was " |
| "not recognized", argv[2]); |
| goto write_msg; |
| } |
| if (replace[i].flags & OPT_KERNELDATA) { |
| ebt_print_error("ebtablesd: table %s needs to " |
| "be freed before it can be " |
| "opened"); |
| goto write_msg; |
| } |
| if (!ebt_get_kernel_table(&replace[i], 0)) { |
| replace[i].flags |= OPT_KERNELDATA; |
| open_method[i] = OPEN_METHOD_KERNEL; |
| } |
| goto write_msg; |
| } else if (!strcmp(argv[1], "fopen")) { |
| struct ebt_u_replace tmp; |
| |
| memset(&tmp, 0, sizeof(tmp)); |
| if (argc != 4) { |
| ebt_print_error("ebtablesd: command fopen " |
| "needs exactly two arguments"); |
| goto write_msg; |
| } |
| |
| for (i = 0; i < 3; i++) |
| if (!strcmp(replace[i].name, argv[2])) |
| break; |
| if (i == 3) { |
| ebt_print_error("ebtablesd: table '%s' was " |
| "not recognized", argv[2]); |
| goto write_msg; |
| } |
| if (replace[i].flags & OPT_KERNELDATA) { |
| ebt_print_error("ebtablesd: table %s needs to " |
| "be freed before it can be " |
| "opened"); |
| goto write_msg; |
| } |
| tmp.filename = (char *)malloc(strlen(argv[3]) + 1); |
| if (!tmp.filename) { |
| ebt_print_error("Out of memory"); |
| goto write_msg; |
| } |
| strcpy(tmp.filename, argv[3]); |
| strcpy(tmp.name, "filter"); |
| tmp.command = 'L'; /* Make sure retrieve_from_file() |
| * doesn't complain about wrong |
| * table name */ |
| |
| ebt_get_kernel_table(&tmp, 0); |
| free(tmp.filename); |
| tmp.filename = NULL; |
| if (ebt_errormsg[0] != '\0') |
| goto write_msg; |
| |
| if (strcmp(tmp.name, argv[2])) { |
| ebt_print_error("ebtablesd: opened file with " |
| "wrong table name '%s'", tmp.name); |
| ebt_cleanup_replace(&tmp); |
| goto write_msg; |
| } |
| replace[i] = tmp; |
| replace[i].command = '\0'; |
| replace[i].flags |= OPT_KERNELDATA; |
| open_method[i] = OPEN_METHOD_FILE; |
| goto write_msg; |
| } else if (!strcmp(argv[1], "commit")) { |
| if (argc != 3) { |
| ebt_print_error("ebtablesd: command commit " |
| "needs exactly one argument"); |
| goto write_msg; |
| } |
| |
| for (i = 0; i < 3; i++) |
| if (!strcmp(replace[i].name, argv[2])) |
| break; |
| if (i == 3) { |
| ebt_print_error("ebtablesd: table '%s' was " |
| "not recognized", argv[2]); |
| goto write_msg; |
| } |
| if (!(replace[i].flags & OPT_KERNELDATA)) { |
| ebt_print_error("ebtablesd: table %s has not " |
| "been opened"); |
| goto write_msg; |
| } |
| /* The counters from the kernel are useless if we |
| * didn't start from a kernel table */ |
| if (open_method[i] == OPEN_METHOD_FILE) |
| replace[i].num_counters = 0; |
| ebt_deliver_table(&replace[i]); |
| if (ebt_errormsg[0] == '\0' && open_method[i] == OPEN_METHOD_KERNEL) |
| ebt_deliver_counters(&replace[i]); |
| goto write_msg; |
| } else if (!strcmp(argv[1], "fcommit")) { |
| if (argc != 4) { |
| ebt_print_error("ebtablesd: command commit " |
| "needs exactly two argument"); |
| goto write_msg; |
| } |
| |
| for (i = 0; i < 3; i++) |
| if (!strcmp(replace[i].name, argv[2])) |
| break; |
| if (i == 3) { |
| ebt_print_error("ebtablesd: table '%s' was " |
| "not recognized", argv[2]); |
| goto write_msg; |
| } |
| if (!(replace[i].flags & OPT_KERNELDATA)) { |
| ebt_print_error("ebtablesd: table %s has not " |
| "been opened"); |
| goto write_msg; |
| } |
| replace[i].filename = (char *)malloc(strlen(argv[3]) + 1); |
| if (!replace[i].filename) { |
| ebt_print_error("Out of memory"); |
| goto write_msg; |
| } |
| strcpy(replace[i].filename, argv[3]); |
| ebt_deliver_table(&replace[i]); |
| if (ebt_errormsg[0] == '\0' && open_method[i] == OPEN_METHOD_KERNEL) |
| ebt_deliver_counters(&replace[i]); |
| free(replace[i].filename); |
| replace[i].filename = NULL; |
| goto write_msg; |
| }else if (!strcmp(argv[1], "quit")) { |
| if (argc != 2) { |
| ebt_print_error("ebtablesd: command quit does " |
| "not take any arguments"); |
| goto write_msg; |
| } |
| break; |
| } |
| if (!(replace[table_nr].flags & OPT_KERNELDATA)) { |
| ebt_print_error("ebtablesd: table %s has not been " |
| "opened", replace[table_nr].name); |
| goto write_msg; |
| } |
| optind = 0; /* Setting optind = 1 causes serious annoyances */ |
| do_command(argc, argv, EXEC_STYLE_DAEMON, &replace[table_nr]); |
| ebt_reinit_extensions(); |
| write_msg: |
| #ifndef SILENT_DAEMON |
| if (ebt_errormsg[0] != '\0') |
| printf("%s.\n", ebt_errormsg); |
| #endif |
| ebt_errormsg[0]= '\0'; |
| n = 0; |
| goto continue_read; |
| } |
| do_exit: |
| unlink(EBTD_PIPE); |
| |
| return 0; |
| } |