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