/* * 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 #include #include #include #include #include #include #include #include #include #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, stop = 0, base = 0, offset = 0, n = 0, ret = 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"); ret = -1; goto do_exit; } if ((readfd = open(EBTD_PIPE, O_RDONLY | O_NONBLOCK, 0)) == -1) { perror("open"); ret = -1; goto do_exit; } if (signal(SIGPIPE, sigpipe_handler) == SIG_ERR) { perror("signal"); ret = -1; goto do_exit; } ebt_silent = 1; copy_table_names(); ebt_early_init_once(); while (!stop) { 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: /* Change all ' ' into '\0'. This implies that the user is not * allowed to use spaces (that don't distinguish options or * commands) in her rules. This comes down to not allowing spaces * in options like the string of --ulog-prefix (use '_' instead). */ for (; offset < ntot; n++, offset++) { if (cmdline[offset] == ' ') cmdline[offset] = '\0'; if (cmdline[offset] == '\n') { 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) { 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], EXEC_STYLE_DAEMON); 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], EXEC_STYLE_DAEMON); 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; } stop = 1; goto write_msg; } 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; }