--- Makefile +++ Makefile @@ -107,6 +107,12 @@ $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS) endif +iptables-batch: iptables-batch.c iptables.o xtables.o $(STATIC_LIBS) libiptc/libiptc.a + $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS) + +ip6tables-batch: iptables-batch.c ip6tables.o xtables.o $(STATIC6_LIBS) libiptc/libiptc.a + $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS) + $(DESTDIR)$(BINDIR)/iptables: iptables @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR) cp $< $@ --- iptables-batch.c +++ iptables-batch.c @@ -0,0 +1,454 @@ +/* + * Author: Ludwig Nussel + * + * Based on the ipchains code by Paul Russell and Michael Neuling + * + * (C) 2000-2002 by the netfilter coreteam : + * Paul 'Rusty' Russell + * Marc Boucher + * James Morris + * Harald Welte + * Jozsef Kadlecsik + * + * iptables-batch -- iptables batch processor + * + * See the accompanying manual page iptables(8) for information + * about proper usage of this program. + * + * 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. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#ifdef IP6T_LIB_DIR +#include +#else +#include +#endif + +static char* errstr = NULL; + +static unsigned current_line = 0; + +static char* +skipspace(char* ptr) +{ + while(*ptr && isspace(*ptr)) + ++ptr; + return ptr; +} + +static char* +getliteral(char** ptr) +{ + char* start = *ptr; + char* p = start; + + while(*p && !isspace(*p)) + ++p; + + if(*p) + { + *p = '\0'; + ++p; + } + + *ptr = p; + return start; +} + +static char* +getstring(char** ptr) +{ + char* start = *ptr+1; // skip leading " + char* p = start; + char* o = start; + int backslash = 0; + int done = 0; + + while(*p && !done) + { + if(backslash) + { + backslash = 0; + // no escapes supported, just eat the backslash + *o++ = *p++; + } + else if(*p == '\\') + { + backslash = 1; + p++; + } + else if(*p == '"') + { + done = 1; + } + else + { + *o++ = *p++; + } + } + + if(done) + { + *o = '\0'; + *p = '\0'; + ++p; + *ptr = p; + } + else + { + errstr = "missing \" at end of string"; + start = NULL; + } + return start; +} + +// this is just a very basic method, not 100% shell compatible +static char* +getword(char** ptr) +{ + *ptr = skipspace(*ptr); + if(**ptr == '"') + return getstring(ptr); + return getliteral(ptr); +} + +// destructive +static int +tokenize(int* argc, char* argv[], size_t nargvsize, char* line) +{ + char* ptr = skipspace(line); + int ret = 0; + char* word; + + while(ptr && *ptr) + { + if(*ptr == '#') + break; + if(*argc >= nargvsize) + { + errstr = "too many arguments"; + ret = -1; + break; + } + word = getword(&ptr); + if(!word) + { + ret = -1; + break; + } + argv[(*argc)++] = word; + ++ret; + } + return ret; +} + +#ifdef DEBUG +static void +dumpargv(int argc, char* argv[]) +{ + int i; + for(i=0; i < argc; ++i) + { + printf("%s\"%s\"",i?" ":"", argv[i]); + } + puts(""); +} +#endif + +struct table_handle +{ + char* name; +#ifdef IP6T_LIB_DIR + ip6tc_handle_t handle; +#else + iptc_handle_t handle; +#endif +}; + +static struct table_handle* tables = NULL; +static unsigned num_tables; +struct table_handle* current_table; + +static void +alloc_tables() +{ + tables = realloc(tables, sizeof(struct table_handle) * num_tables); +} + +static void +set_current_table(const char* name) +{ + unsigned i; + + if(!strcmp(name, current_table->name)) // same as last time? + return; + + for(i = 0; i < num_tables; ++i) // find already known table + { + if(!strcmp(name, tables[i].name)) + { + current_table = &tables[i]; + return; + } + } + + // table name not known, create new + i = num_tables++; + alloc_tables(); + current_table = &tables[i]; + current_table->name = strdup(name); + current_table->handle = NULL; +} + +static int +find_table(int argc, char* argv[]) +{ + int i; + for(i = 0; i < argc; ++i) + { + if(!strcmp(argv[i], "-t") || !strcmp(argv[i], "--table")) + { + ++i; + if(i >= argc) + { + fprintf(stderr, "line %d: missing table name after %s\n", + current_line, argv[i]); + return 0; + } + set_current_table(argv[i]); + return 1; + } + } + + // no -t specified + set_current_table("filter"); + + return 1; +} + +static int +do_iptables(int argc, char* argv[]) +{ + char *table = "filter"; + int ret = 0; + + if(!find_table(argc, argv)) + return 0; + +#ifdef IP6T_LIB_DIR + ret = do_command6(argc, argv, &table, ¤t_table->handle); + + if (!ret) + { + fprintf(stderr, "line %d: %s\n", current_line, ip6tc_strerror(errno)); + } + else + { + if(!table || strcmp(table, current_table->name)) + { + fprintf(stderr, "line %d: expected table %s, got %s\n", + current_line, current_table->name, table); + exit(1); + } + } +#else + ret = do_command(argc, argv, &table, ¤t_table->handle); + + if (!ret) + { + fprintf(stderr, "line %d: %s\n", current_line, iptc_strerror(errno)); + } + else + { + if(!table || strcmp(table, current_table->name)) + { + fprintf(stderr, "line %d: expected table %s, got %s\n", + current_line, current_table->name, table); + exit(1); + } + } +#endif + + return ret; +} + +static int +do_commit() +{ + unsigned i; + int ret = 1; + + for(i = 0; i < num_tables; ++i) + { + if(tables[i].handle) + { +#ifdef IP6T_LIB_DIR + if(!ip6tc_commit(&tables[i].handle)) + { + fprintf(stderr, "commit failed on table %s: %s\n", tables[i].name, ip6tc_strerror(errno)); + ret = 0; + } +#else + if(!iptc_commit(&tables[i].handle)) + { + fprintf(stderr, "commit failed on table %s: %s\n", tables[i].name, iptc_strerror(errno)); + ret = 0; + } +#endif + } + } + + return ret; +} + +static void +help() +{ + fprintf(stderr, "Usage: %s [FILE]\n\n", program_name); + puts("Read iptables commands from FILE, commit them at EOF\n"); + puts("In addition to normal iptables calls the commands"); + puts("'commit' and 'exit' are understood."); + exit(0); +} + +int +main(int argc, char *argv[]) +{ + int ret = 1; + int numtok; + size_t llen = 0; + char* line = NULL; + ssize_t r = -1; + int nargc = 0; + char* nargv[256]; + FILE* fp = stdin; + +#ifdef IP6T_LIB_DIR + program_name = "ip6tables-batch"; + + lib_dir = getenv("IP6TABLES_LIB_DIR"); + if (!lib_dir) + lib_dir = IP6T_LIB_DIR; +#else + program_name = "iptables-batch"; + + lib_dir = getenv("IPTABLES_LIB_DIR"); + if (!lib_dir) + lib_dir = IPT_LIB_DIR; +#endif + program_version = IPTABLES_VERSION; + +#ifdef NO_SHARED_LIBS + init_extensions(); +#endif + if(argc > 1) + { + if(!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) + { + help(); + } + else if(strcmp(argv[1], "-")) + { + fp = fopen(argv[1], "r"); + if(!fp) + { + perror("fopen"); + exit(1); + } + } + } + + num_tables = 4; + alloc_tables(); + tables[0].name = "filter"; + tables[0].handle = NULL; + tables[1].name = "mangle"; + tables[1].handle = NULL; + tables[2].name = "nat"; + tables[2].handle = NULL; + tables[3].name = "raw"; + tables[3].handle = NULL; + current_table = &tables[0]; + + while((r = getline(&line, &llen, fp)) != -1) + { + if(llen < 1 || !*line) + continue; + if(line[strlen(line)-1] == '\n') + line[strlen(line) -1 ] = '\0'; + + ++current_line; + nargc = 0; + errstr = NULL; + numtok = tokenize(&nargc, nargv, (sizeof(nargv)/sizeof(nargv[0])), line); + if(numtok == -1) + { + } + else if (numtok == 0) + { + continue; + } + else if(nargc < 1) + { + errstr = "insufficient number of arguments"; + } + + if(errstr) + { + fprintf(stderr, "parse error in line %d: %s\n", current_line, errstr); + ret = 0; + break; + } + +#ifdef DEBUG + dumpargv(nargc, nargv); +#endif + +#ifdef IP6T_LIB_DIR + if(!strcmp(nargv[0], "ip6tables")) +#else + if(!strcmp(nargv[0], "iptables")) +#endif + { + ret = do_iptables(nargc, nargv); + if(!ret) break; + } + else if(!strcmp(nargv[0], "exit")) + { + break; + } + else if(!strcmp(nargv[0], "commit")) + { + ret = do_commit(); + if(!ret) break; + } + else + { + fprintf(stderr, "line %d: invalid command '%s'\n", current_line, nargv[0]); + } + } + + if(ret) + ret = do_commit(); + + exit(!ret); +}