diff options
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | Makefile.am | 40 | ||||
-rw-r--r-- | extensions/GNUmakefile.in | 59 | ||||
-rw-r--r-- | extensions/libipt_DNAT.c | 2 | ||||
-rw-r--r-- | extensions/libipt_DNAT.man | 2 | ||||
-rw-r--r-- | extensions/libipt_SNAT.c | 2 | ||||
-rw-r--r-- | extensions/libipt_SNAT.man | 2 | ||||
-rw-r--r-- | extensions/libxt_SET.c | 207 | ||||
-rw-r--r-- | extensions/libxt_SET.man | 8 | ||||
-rw-r--r-- | extensions/libxt_set.c | 52 | ||||
-rw-r--r-- | extensions/libxt_set.h | 7 | ||||
-rw-r--r-- | include/ip6tables.h | 8 | ||||
-rw-r--r-- | include/iptables.h | 10 | ||||
-rw-r--r-- | include/linux/netfilter/xt_set.h | 20 | ||||
-rw-r--r-- | include/xtables.h.in | 2 | ||||
-rw-r--r-- | ip6tables-multi.c | 23 | ||||
-rw-r--r-- | ip6tables-restore.c | 5 | ||||
-rw-r--r-- | ip6tables-save.c | 3 | ||||
-rw-r--r-- | ip6tables-standalone.c | 1 | ||||
-rw-r--r-- | ip6tables.c | 47 | ||||
-rw-r--r-- | iptables-multi.c | 26 | ||||
-rw-r--r-- | iptables-restore.c | 9 | ||||
-rw-r--r-- | iptables-save.c | 3 | ||||
-rw-r--r-- | iptables-standalone.c | 3 | ||||
-rw-r--r-- | iptables-xml.c | 2 | ||||
-rw-r--r-- | iptables.c | 48 | ||||
-rw-r--r-- | xshared.c | 2 | ||||
-rw-r--r-- | xshared.h | 2 | ||||
-rw-r--r-- | xtables-multi.c | 39 | ||||
-rw-r--r-- | xtables.c | 104 |
30 files changed, 517 insertions, 225 deletions
@@ -13,6 +13,7 @@ Makefile Makefile.in /extensions/GNUmakefile +/extensions/initext.c /extensions/initext?.c /extensions/matches?.man /extensions/targets?.man @@ -40,14 +41,13 @@ Makefile.in /ip6tables /ip6tables.8 -/ip6tables-multi /ip6tables-save /ip6tables-restore /ip6tables-static /iptables /iptables.8 -/iptables-multi /iptables-save /iptables-restore /iptables-static /iptables-xml +/xtables-multi diff --git a/Makefile.am b/Makefile.am index fbed41fc..13e144e4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -17,7 +17,7 @@ endif lib_LTLIBRARIES = # libiptc -lib_LTLIBRARIES += libiptc/libip4tc.la libiptc/libip6tc.la libiptc/libiptc.la +lib_LTLIBRARIES += libiptc/libip4tc.la libiptc/libip6tc.la libiptc/libiptc.la libiptc_libiptc_la_SOURCES = libiptc_libiptc_la_LIBADD = libiptc/libip4tc.la libiptc/libip6tc.la libiptc_libiptc_la_LDFLAGS = -version-info 0:0:0 ${libiptc_LDFLAGS2} @@ -37,39 +37,39 @@ libxtables_la_CFLAGS = ${AM_CFLAGS} -DNO_SHARED_LIBS=1 libxtables_la_LIBADD = endif -iptables_multi_SOURCES = iptables-multi.c iptables-save.c \ - iptables-restore.c iptables-xml.c \ - iptables-standalone.c iptables.c xshared.c -iptables_multi_CFLAGS = ${AM_CFLAGS} -DIPTABLES_MULTI +xtables_multi_SOURCES = xtables-multi.c +xtables_multi_CFLAGS = ${AM_CFLAGS} -DIPTABLES_MULTI +xtables_multi_LDFLAGS = -rdynamic +xtables_multi_LDADD = extensions/libext.a if ENABLE_STATIC -iptables_multi_CFLAGS += -DALL_INCLUSIVE +xtables_multi_CFLAGS += -DALL_INCLUSIVE endif -iptables_multi_LDFLAGS = -rdynamic -iptables_multi_LDADD = libiptc/libip4tc.la extensions/libext4.a libxtables.la -lm - -ip6tables_multi_SOURCES = ip6tables-multi.c ip6tables-save.c \ - ip6tables-restore.c ip6tables-standalone.c \ - ip6tables.c xshared.c -ip6tables_multi_CFLAGS = ${AM_CFLAGS} -DIPTABLES_MULTI -if ENABLE_STATIC -ip6tables_multi_CFLAGS += -DALL_INCLUSIVE +if ENABLE_IPV4 +xtables_multi_SOURCES += iptables-save.c iptables-restore.c iptables-xml.c \ + iptables-standalone.c iptables.c +xtables_multi_CFLAGS += -DENABLE_IPV4 +xtables_multi_LDADD += libiptc/libip4tc.la extensions/libext4.a +endif +if ENABLE_IPV6 +xtables_multi_SOURCES += ip6tables-save.c ip6tables-restore.c \ + ip6tables-standalone.c ip6tables.c +xtables_multi_CFLAGS += -DENABLE_IPV6 +xtables_multi_LDADD += libiptc/libip6tc.la extensions/libext6.a endif -ip6tables_multi_LDFLAGS = -rdynamic -ip6tables_multi_LDADD = libiptc/libip6tc.la extensions/libext6.a libxtables.la -lm +xtables_multi_SOURCES += xshared.c +xtables_multi_LDADD += libxtables.la -lm -sbin_PROGRAMS = +sbin_PROGRAMS = xtables-multi man_MANS = iptables.8 iptables-restore.8 iptables-save.8 \ iptables-xml.8 ip6tables.8 ip6tables-restore.8 \ ip6tables-save.8 CLEANFILES = iptables.8 ip6tables.8 if ENABLE_IPV4 -sbin_PROGRAMS += iptables-multi v4_bin_links = iptables-xml v4_sbin_links = iptables iptables-restore iptables-save endif if ENABLE_IPV6 -sbin_PROGRAMS += ip6tables-multi v6_sbin_links = ip6tables ip6tables-restore ip6tables-save endif diff --git a/extensions/GNUmakefile.in b/extensions/GNUmakefile.in index b96bd11f..a511a39a 100644 --- a/extensions/GNUmakefile.in +++ b/extensions/GNUmakefile.in @@ -51,11 +51,13 @@ pf6_solibs := $(patsubst %,libip6t_%.so,${pf6_build_mod}) # # Building blocks # -targets := libext4.a libext6.a matches4.man matches6.man \ +targets := libext.a libext4.a libext6.a \ + matches4.man matches6.man \ targets4.man targets6.man targets_install := -@ENABLE_STATIC_TRUE@ libext4_objs := ${pfx_objs} ${pf4_objs} -@ENABLE_STATIC_TRUE@ libext6_objs := ${pfx_objs} ${pf6_objs} +@ENABLE_STATIC_TRUE@ libext_objs := ${pfx_objs} +@ENABLE_STATIC_TRUE@ libext4_objs := ${pf4_objs} +@ENABLE_STATIC_TRUE@ libext6_objs := ${pf6_objs} @ENABLE_STATIC_FALSE@ targets += ${pfx_solibs} ${pf4_solibs} ${pf6_solibs} @ENABLE_STATIC_FALSE@ targets_install += ${pfx_solibs} ${pf4_solibs} ${pf6_solibs} @@ -70,7 +72,7 @@ install: ${targets_install} if test -n "${targets_install}"; then install -pm0755 $^ "${DESTDIR}${xtlibdir}/"; fi; clean: - rm -f *.o *.oo *.so *.a {matches,targets}[46].man initext4.c initext6.c; + rm -f *.o *.oo *.so *.a {matches,targets}[46].man initext.c initext4.c initext6.c; distclean: clean rm -f .*.d .*.dd; @@ -101,26 +103,35 @@ lib%.oo: ${srcdir}/lib%.c lib%.o: ${srcdir}/lib%.c ${AM_VERBOSE_CC} ${CC} ${AM_DEPFLAGS} ${AM_CFLAGS} -DNO_SHARED_LIBS=1 -D_INIT=lib$*_init ${CFLAGS} -o $@ -c $<; +libext.a: initext.o ${libext_objs} + ${AM_VERBOSE_AR} ${AR} crs $@ $^; + libext4.a: initext4.o ${libext4_objs} ${AM_VERBOSE_AR} ${AR} crs $@ $^; libext6.a: initext6.o ${libext6_objs} ${AM_VERBOSE_AR} ${AR} crs $@ $^; -initext_func := $(addprefix xt_,${pfx_build_mod}) $(addprefix ipt_,${pf4_build_mod}) -initext6_func := $(addprefix xt_,${pfx_build_mod}) $(addprefix ip6t_,${pf6_build_mod}) +initext_func := $(addprefix xt_,${pfx_build_mod}) +initext4_func := $(addprefix ipt_,${pf4_build_mod}) +initext6_func := $(addprefix ip6t_,${pf6_build_mod}) -.initext4.dd: FORCE +.initext.dd: FORCE @echo "${initext_func}" >$@.tmp; \ cmp -s $@ $@.tmp || mv $@.tmp $@; \ rm -f $@.tmp; +.initext4.dd: FORCE + @echo "${initext4_func}" >$@.tmp; \ + cmp -s $@ $@.tmp || mv $@.tmp $@; \ + rm -f $@.tmp; + .initext6.dd: FORCE @echo "${initext6_func}" >$@.tmp; \ cmp -s $@ $@.tmp || mv $@.tmp $@; \ rm -f $@.tmp; -initext4.c: .initext4.dd +initext.c: .initext.dd ${AM_VERBOSE_GEN} @( \ echo "" >$@; \ @@ -136,6 +147,22 @@ initext4.c: .initext4.dd echo "}" >>$@; \ ); +initext4.c: .initext4.dd + ${AM_VERBOSE_GEN} + @( \ + echo "" >$@; \ + for i in ${initext4_func}; do \ + echo "extern void lib$${i}_init(void);" >>$@; \ + done; \ + echo "void init_extensions4(void);" >>$@; \ + echo "void init_extensions4(void)" >>$@; \ + echo "{" >>$@; \ + for i in ${initext4_func}; do \ + echo " ""lib$${i}_init();" >>$@; \ + done; \ + echo "}" >>$@; \ + ); + initext6.c: .initext6.dd ${AM_VERBOSE_GEN} @( \ @@ -143,8 +170,8 @@ initext6.c: .initext6.dd for i in ${initext6_func}; do \ echo "extern void lib$${i}_init(void);" >>$@; \ done; \ - echo "void init_extensions(void);" >>$@; \ - echo "void init_extensions(void)" >>$@; \ + echo "void init_extensions6(void);" >>$@; \ + echo "void init_extensions6(void)" >>$@; \ echo "{" >>$@; \ for i in ${initext6_func}; do \ echo " ""lib$${i}_init();" >>$@; \ @@ -155,8 +182,8 @@ initext6.c: .initext6.dd # # Manual pages # -ex_matches = $(sort $(shell echo $(1) | LC_ALL=POSIX grep -Eo '\b[[:alnum:]_]+\b')) -ex_targets = $(sort $(shell echo $(1) | LC_ALL=POSIX grep -Eo '\b[[:alnum:]_]+\b')) +ex_matches = $(sort $(shell echo $(1) | LC_ALL=POSIX grep -Eo '\b[[:lower:][:digit:]_]+\b')) +ex_targets = $(sort $(shell echo $(1) | LC_ALL=POSIX grep -Eo '\b[[:upper:][:digit:]_]+\b')) man_run = \ ${AM_VERBOSE_GEN} \ for ext in $(1); do \ @@ -177,14 +204,14 @@ man_run = \ fi; \ done >$@; -matches4.man: .initext4.dd $(wildcard ${srcdir}/lib*.man) +matches4.man: .initext.dd .initext4.dd $(wildcard ${srcdir}/lib*.man) $(call man_run,$(call ex_matches,${pfx_build_mod} ${pf4_build_mod}),ip,NFPROTO_IPV4) -matches6.man: .initext6.dd $(wildcard ${srcdir}/lib*.man) +matches6.man: .initext.dd .initext6.dd $(wildcard ${srcdir}/lib*.man) $(call man_run,$(call ex_matches,${pfx_build_mod} ${pf6_build_mod}),ip6,NFPROTO_IPV6) -targets4.man: .initext4.dd $(wildcard ${srcdir}/lib*.man) +targets4.man: .initext.dd .initext4.dd $(wildcard ${srcdir}/lib*.man) $(call man_run,$(call ex_targets,${pfx_build_mod} ${pf4_build_mod}),ip,NFPROTO_IPV4) -targets6.man: .initext6.dd $(wildcard ${srcdir}/lib*.man) +targets6.man: .initext.dd .initext6.dd $(wildcard ${srcdir}/lib*.man) $(call man_run,$(call ex_targets,${pfx_build_mod} ${pf6_build_mod}),ip6,NFPROTO_IPV6) diff --git a/extensions/libipt_DNAT.c b/extensions/libipt_DNAT.c index 49c74e10..6eedca10 100644 --- a/extensions/libipt_DNAT.c +++ b/extensions/libipt_DNAT.c @@ -26,7 +26,7 @@ static void DNAT_help(void) { printf( "DNAT target options:\n" -" --to-destination <ipaddr>[-<ipaddr>][:port-port]\n" +" --to-destination [<ipaddr>[-<ipaddr>]][:port[-port]]\n" " Address to map destination to.\n" "[--random] [--persistent]\n"); } diff --git a/extensions/libipt_DNAT.man b/extensions/libipt_DNAT.man index d1e0a3a1..d5ded35b 100644 --- a/extensions/libipt_DNAT.man +++ b/extensions/libipt_DNAT.man @@ -10,7 +10,7 @@ should be modified (and all future packets in this connection will also be mangled), and rules should cease being examined. It takes one type of option: .TP -\fB\-\-to\-destination\fP [\fIipaddr\fP][\fB\-\fP\fIipaddr\fP][\fB:\fP\fIport\fP[\fB\-\fP\fIport\fP]] +\fB\-\-to\-destination\fP [\fIipaddr\fP[\fB\-\fP\fIipaddr\fP]][\fB:\fP\fIport\fP[\fB\-\fP\fIport\fP]] which can specify a single new destination IP address, an inclusive range of IP addresses, and optionally, a port range (which is only valid if the rule also specifies diff --git a/extensions/libipt_SNAT.c b/extensions/libipt_SNAT.c index 532770da..1a009548 100644 --- a/extensions/libipt_SNAT.c +++ b/extensions/libipt_SNAT.c @@ -26,7 +26,7 @@ static void SNAT_help(void) { printf( "SNAT target options:\n" -" --to-source <ipaddr>[-<ipaddr>][:port-port]\n" +" --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n" " Address to map source to.\n" "[--random] [--persistent]\n"); } diff --git a/extensions/libipt_SNAT.man b/extensions/libipt_SNAT.man index 6b828fd4..80e2cb91 100644 --- a/extensions/libipt_SNAT.man +++ b/extensions/libipt_SNAT.man @@ -7,7 +7,7 @@ modified (and all future packets in this connection will also be mangled), and rules should cease being examined. It takes one type of option: .TP -\fB\-\-to\-source\fP \fIipaddr\fP[\fB\-\fP\fIipaddr\fP][\fB:\fP\fIport\fP[\fB\-\fP\fIport\fP]] +\fB\-\-to\-source\fP [\fIipaddr\fP[\fB\-\fP\fIipaddr\fP]][\fB:\fP\fIport\fP[\fB\-\fP\fIport\fP]] which can specify a single new source IP address, an inclusive range of IP addresses, and optionally, a port range (which is only valid if the rule also specifies diff --git a/extensions/libxt_SET.c b/extensions/libxt_SET.c index 2f915bcd..51c0cec6 100644 --- a/extensions/libxt_SET.c +++ b/extensions/libxt_SET.c @@ -21,8 +21,10 @@ #include <linux/netfilter/xt_set.h> #include "libxt_set.h" +/* Revision 0 */ + static void -set_target_help(void) +set_target_help_v0(void) { printf("SET target options:\n" " --add-set name flags\n" @@ -32,14 +34,14 @@ set_target_help(void) " 'src' and 'dst' specifications.\n"); } -static const struct option set_target_opts[] = { +static const struct option set_target_opts_v0[] = { {.name = "add-set", .has_arg = true, .val = '1'}, {.name = "del-set", .has_arg = true, .val = '2'}, XT_GETOPT_TABLEEND, }; static void -set_target_check(unsigned int flags) +set_target_check_v0(unsigned int flags) { if (!flags) xtables_error(PARAMETER_PROBLEM, @@ -144,20 +146,29 @@ set_target_save_v0(const void *ip, const struct xt_entry_target *target) print_target_v0("--del-set", &info->del_set); } +/* Revision 1 */ + +#define set_target_help_v1 set_target_help_v0 + static void -set_target_init(struct xt_entry_target *target) +set_target_init_v1(struct xt_entry_target *target) { - struct xt_set_info_target *info = - (struct xt_set_info_target *) target->data; + struct xt_set_info_target_v1 *info = + (struct xt_set_info_target_v1 *) target->data; info->add_set.index = info->del_set.index = IPSET_INVALID_ID; } +#define SET_TARGET_ADD 0x1 +#define SET_TARGET_DEL 0x2 +#define SET_TARGET_EXIST 0x4 +#define SET_TARGET_TIMEOUT 0x8 + static void -parse_target(char **argv, int invert, unsigned int *flags, - struct xt_set_info *info, const char *what) +parse_target(char **argv, int invert, struct xt_set_info *info, + const char *what) { if (info->dim) xtables_error(PARAMETER_PROBLEM, @@ -180,30 +191,30 @@ parse_target(char **argv, int invert, unsigned int *flags, get_set_byname(optarg, info); parse_dirs(argv[optind], info); optind++; - - *flags = 1; } static int -set_target_parse(int c, char **argv, int invert, unsigned int *flags, - const void *entry, struct xt_entry_target **target) +set_target_parse_v1(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) { - struct xt_set_info_target *myinfo = - (struct xt_set_info_target *) (*target)->data; + struct xt_set_info_target_v1 *myinfo = + (struct xt_set_info_target_v1 *) (*target)->data; switch (c) { case '1': /* --add-set <set> <flags> */ - parse_target(argv, invert, flags, - &myinfo->add_set, "add-set"); + parse_target(argv, invert, &myinfo->add_set, "add-set"); + *flags |= SET_TARGET_ADD; break; case '2': /* --del-set <set>[:<flags>] <flags> */ - parse_target(argv, invert, flags, - &myinfo->del_set, "del-set"); + parse_target(argv, invert, &myinfo->del_set, "del-set"); + *flags |= SET_TARGET_DEL; break; } return 1; } +#define set_target_check_v1 set_target_check_v0 + static void print_target(const char *prefix, const struct xt_set_info *info) { @@ -222,21 +233,132 @@ print_target(const char *prefix, const struct xt_set_info *info) } static void -set_target_print(const void *ip, const struct xt_entry_target *target, - int numeric) +set_target_print_v1(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_set_info_target_v1 *info = (const void *)target->data; + + print_target("add-set", &info->add_set); + print_target("del-set", &info->del_set); +} + +static void +set_target_save_v1(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_set_info_target_v1 *info = (const void *)target->data; + + print_target("--add-set", &info->add_set); + print_target("--del-set", &info->del_set); +} + +#define set_target_opts_v1 set_target_opts_v0 + +/* Revision 2 */ + +static void +set_target_help_v2(void) +{ + printf("SET target options:\n" + " --add-set name flags [--exist] [--timeout n]\n" + " --del-set name flags\n" + " add/del src/dst IP/port from/to named sets,\n" + " where flags are the comma separated list of\n" + " 'src' and 'dst' specifications.\n"); +} + +static const struct option set_target_opts_v2[] = { + {.name = "add-set", .has_arg = true, .val = '1'}, + {.name = "del-set", .has_arg = true, .val = '2'}, + {.name = "exist", .has_arg = false, .val = '3'}, + {.name = "timeout", .has_arg = true, .val = '4'}, + XT_GETOPT_TABLEEND, +}; + +static void +set_target_check_v2(unsigned int flags) +{ + if (!(flags & (SET_TARGET_ADD|SET_TARGET_DEL))) + xtables_error(PARAMETER_PROBLEM, + "You must specify either `--add-set' or `--del-set'"); + if (!(flags & SET_TARGET_ADD)) { + if (flags & SET_TARGET_EXIST) + xtables_error(PARAMETER_PROBLEM, + "Flag `--exist' can be used with `--add-set' only"); + if (flags & SET_TARGET_TIMEOUT) + xtables_error(PARAMETER_PROBLEM, + "Option `--timeout' can be used with `--add-set' only"); + } +} + + +static void +set_target_init_v2(struct xt_entry_target *target) { - const struct xt_set_info_target *info = (const void *)target->data; + struct xt_set_info_target_v2 *info = + (struct xt_set_info_target_v2 *) target->data; + + info->add_set.index = + info->del_set.index = IPSET_INVALID_ID; + info->timeout = UINT32_MAX; +} + +static int +set_target_parse_v2(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_set_info_target_v2 *myinfo = + (struct xt_set_info_target_v2 *) (*target)->data; + unsigned int timeout; + + switch (c) { + case '1': /* --add-set <set> <flags> */ + parse_target(argv, invert, &myinfo->add_set, "add-set"); + *flags |= SET_TARGET_ADD; + break; + case '2': /* --del-set <set>[:<flags>] <flags> */ + parse_target(argv, invert, &myinfo->del_set, "del-set"); + *flags |= SET_TARGET_DEL; + break; + case '3': + myinfo->flags |= IPSET_FLAG_EXIST; + *flags |= SET_TARGET_EXIST; + break; + case '4': + if (!xtables_strtoui(optarg, NULL, &timeout, 0, UINT32_MAX - 1)) + xtables_error(PARAMETER_PROBLEM, + "Invalid value for option --timeout " + "or out of range 0-%u", UINT32_MAX - 1); + myinfo->timeout = timeout; + *flags |= SET_TARGET_TIMEOUT; + break; + } + return 1; +} + +static void +set_target_print_v2(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_set_info_target_v2 *info = (const void *)target->data; print_target("add-set", &info->add_set); + if (info->flags & IPSET_FLAG_EXIST) + printf(" exist"); + if (info->timeout != UINT32_MAX) + printf(" timeout %u", info->timeout); print_target("del-set", &info->del_set); } static void -set_target_save(const void *ip, const struct xt_entry_target *target) +set_target_save_v2(const void *ip, const struct xt_entry_target *target) { - const struct xt_set_info_target *info = (const void *)target->data; + const struct xt_set_info_target_v2 *info = (const void *)target->data; print_target("--add-set", &info->add_set); + if (info->flags & IPSET_FLAG_EXIST) + printf(" --exist"); + if (info->timeout != UINT32_MAX) + printf(" --timeout %u", info->timeout); print_target("--del-set", &info->del_set); } @@ -248,28 +370,43 @@ static struct xtables_target set_tg_reg[] = { .family = NFPROTO_IPV4, .size = XT_ALIGN(sizeof(struct xt_set_info_target_v0)), .userspacesize = XT_ALIGN(sizeof(struct xt_set_info_target_v0)), - .help = set_target_help, + .help = set_target_help_v0, .init = set_target_init_v0, .parse = set_target_parse_v0, - .final_check = set_target_check, + .final_check = set_target_check_v0, .print = set_target_print_v0, .save = set_target_save_v0, - .extra_opts = set_target_opts, + .extra_opts = set_target_opts_v0, }, { .name = "SET", .revision = 1, .version = XTABLES_VERSION, .family = NFPROTO_UNSPEC, - .size = XT_ALIGN(sizeof(struct xt_set_info_target)), - .userspacesize = XT_ALIGN(sizeof(struct xt_set_info_target)), - .help = set_target_help, - .init = set_target_init, - .parse = set_target_parse, - .final_check = set_target_check, - .print = set_target_print, - .save = set_target_save, - .extra_opts = set_target_opts, + .size = XT_ALIGN(sizeof(struct xt_set_info_target_v1)), + .userspacesize = XT_ALIGN(sizeof(struct xt_set_info_target_v1)), + .help = set_target_help_v1, + .init = set_target_init_v1, + .parse = set_target_parse_v1, + .final_check = set_target_check_v1, + .print = set_target_print_v1, + .save = set_target_save_v1, + .extra_opts = set_target_opts_v1, + }, + { + .name = "SET", + .revision = 2, + .version = XTABLES_VERSION, + .family = NFPROTO_UNSPEC, + .size = XT_ALIGN(sizeof(struct xt_set_info_target_v2)), + .userspacesize = XT_ALIGN(sizeof(struct xt_set_info_target_v2)), + .help = set_target_help_v2, + .init = set_target_init_v2, + .parse = set_target_parse_v2, + .final_check = set_target_check_v2, + .print = set_target_print_v2, + .save = set_target_save_v2, + .extra_opts = set_target_opts_v2, }, }; diff --git a/extensions/libxt_SET.man b/extensions/libxt_SET.man index ea80c2a1..739be414 100644 --- a/extensions/libxt_SET.man +++ b/extensions/libxt_SET.man @@ -12,6 +12,14 @@ where flags are and/or .BR "dst" specifications and there can be no more than six of them. +.TP +\fB\-\-timeout\fP \fIvalue\fP +when adding entry, the timeout value to use instead of the default +one from the set definition +.TP +\fB\-\-exist\fP +when adding entry if it already exists, reset the timeout value +to the specified one or to the default from the set definition .PP Use of -j SET requires that ipset kernel support is provided. As standard kernels do not ship this currently, the ipset or Xtables-addons package needs diff --git a/extensions/libxt_set.c b/extensions/libxt_set.c index 78bf7c10..da722c73 100644 --- a/extensions/libxt_set.c +++ b/extensions/libxt_set.c @@ -22,8 +22,10 @@ #include <linux/netfilter/xt_set.h> #include "libxt_set.h" +/* Revision 0 */ + static void -set_help(void) +set_help_v0(void) { printf("set match options:\n" " [!] --match-set name flags\n" @@ -32,14 +34,14 @@ set_help(void) " 'src' and 'dst' specifications.\n"); } -static const struct option set_opts[] = { +static const struct option set_opts_v0[] = { {.name = "match-set", .has_arg = true, .val = '1'}, {.name = "set", .has_arg = true, .val = '2'}, XT_GETOPT_TABLEEND, }; static void -set_check(unsigned int flags) +set_check_v0(unsigned int flags) { if (!flags) xtables_error(PARAMETER_PROBLEM, @@ -127,12 +129,18 @@ set_save_v0(const void *ip, const struct xt_entry_match *match) print_match_v0("--match-set", &info->match_set); } +/* Revision 1 */ + +#define set_help_v1 set_help_v0 +#define set_opts_v1 set_opts_v0 +#define set_check_v1 set_check_v0 + static int -set_parse(int c, char **argv, int invert, unsigned int *flags, - const void *entry, struct xt_entry_match **match) +set_parse_v1(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) { - struct xt_set_info_match *myinfo = - (struct xt_set_info_match *) (*match)->data; + struct xt_set_info_match_v1 *myinfo = + (struct xt_set_info_match_v1 *) (*match)->data; struct xt_set_info *info = &myinfo->match_set; switch (c) { @@ -191,17 +199,17 @@ print_match(const char *prefix, const struct xt_set_info *info) /* Prints out the matchinfo. */ static void -set_print(const void *ip, const struct xt_entry_match *match, int numeric) +set_print_v1(const void *ip, const struct xt_entry_match *match, int numeric) { - const struct xt_set_info_match *info = (const void *)match->data; + const struct xt_set_info_match_v1 *info = (const void *)match->data; print_match("match-set", &info->match_set); } static void -set_save(const void *ip, const struct xt_entry_match *match) +set_save_v1(const void *ip, const struct xt_entry_match *match) { - const struct xt_set_info_match *info = (const void *)match->data; + const struct xt_set_info_match_v1 *info = (const void *)match->data; print_match("--match-set", &info->match_set); } @@ -214,26 +222,26 @@ static struct xtables_match set_mt_reg[] = { .family = NFPROTO_IPV4, .size = XT_ALIGN(sizeof(struct xt_set_info_match_v0)), .userspacesize = XT_ALIGN(sizeof(struct xt_set_info_match_v0)), - .help = set_help, + .help = set_help_v0, .parse = set_parse_v0, - .final_check = set_check, + .final_check = set_check_v0, .print = set_print_v0, .save = set_save_v0, - .extra_opts = set_opts, + .extra_opts = set_opts_v0, }, { .name = "set", .revision = 1, .version = XTABLES_VERSION, .family = NFPROTO_UNSPEC, - .size = XT_ALIGN(sizeof(struct xt_set_info_match)), - .userspacesize = XT_ALIGN(sizeof(struct xt_set_info_match)), - .help = set_help, - .parse = set_parse, - .final_check = set_check, - .print = set_print, - .save = set_save, - .extra_opts = set_opts, + .size = XT_ALIGN(sizeof(struct xt_set_info_match_v1)), + .userspacesize = XT_ALIGN(sizeof(struct xt_set_info_match_v1)), + .help = set_help_v1, + .parse = set_parse_v1, + .final_check = set_check_v1, + .print = set_print_v1, + .save = set_save_v1, + .extra_opts = set_opts_v1, }, }; diff --git a/extensions/libxt_set.h b/extensions/libxt_set.h index 6b936911..4ac84fa9 100644 --- a/extensions/libxt_set.h +++ b/extensions/libxt_set.h @@ -114,7 +114,7 @@ parse_dirs_v0(const char *opt_arg, struct xt_set_info_v0 *info) if (tmp) xtables_error(PARAMETER_PROBLEM, "Can't be more src/dst options than %i.", - IPSET_DIM_MAX - 1); + IPSET_DIM_MAX); free(saved); } @@ -124,9 +124,8 @@ parse_dirs(const char *opt_arg, struct xt_set_info *info) { char *saved = strdup(opt_arg); char *ptr, *tmp = saved; - int i = 0; - while (i < (IPSET_DIM_MAX - 1) && tmp != NULL) { + while (info->dim < IPSET_DIM_MAX && tmp != NULL) { info->dim++; ptr = strsep(&tmp, ","); if (strncmp(ptr, "src", 3) == 0) @@ -139,7 +138,7 @@ parse_dirs(const char *opt_arg, struct xt_set_info *info) if (tmp) xtables_error(PARAMETER_PROBLEM, "Can't be more src/dst options than %i.", - IPSET_DIM_MAX - 1); + IPSET_DIM_MAX); free(saved); } diff --git a/include/ip6tables.h b/include/ip6tables.h index ca0f9a05..e976361f 100644 --- a/include/ip6tables.h +++ b/include/ip6tables.h @@ -10,10 +10,10 @@ extern int do_command6(int argc, char *argv[], char **table, struct ip6tc_handle **handle); -extern int for_each_chain(int (*fn)(const ip6t_chainlabel, int, struct ip6tc_handle *), int verbose, int builtinstoo, struct ip6tc_handle *handle); -extern int flush_entries(const ip6t_chainlabel chain, int verbose, struct ip6tc_handle *handle); -extern int delete_chain(const ip6t_chainlabel chain, int verbose, struct ip6tc_handle *handle); -void print_rule(const struct ip6t_entry *e, struct ip6tc_handle *h, const char *chain, int counters); +extern int for_each_chain6(int (*fn)(const ip6t_chainlabel, int, struct ip6tc_handle *), int verbose, int builtinstoo, struct ip6tc_handle *handle); +extern int flush_entries6(const ip6t_chainlabel chain, int verbose, struct ip6tc_handle *handle); +extern int delete_chain6(const ip6t_chainlabel chain, int verbose, struct ip6tc_handle *handle); +void print_rule6(const struct ip6t_entry *e, struct ip6tc_handle *h, const char *chain, int counters); extern struct xtables_globals ip6tables_globals; diff --git a/include/iptables.h b/include/iptables.h index 84211c32..65b32909 100644 --- a/include/iptables.h +++ b/include/iptables.h @@ -7,15 +7,15 @@ #include <iptables/internal.h> /* Your shared library should call one of these. */ -extern int do_command(int argc, char *argv[], char **table, +extern int do_command4(int argc, char *argv[], char **table, struct iptc_handle **handle); -extern int delete_chain(const ipt_chainlabel chain, int verbose, +extern int delete_chain4(const ipt_chainlabel chain, int verbose, struct iptc_handle *handle); -extern int flush_entries(const ipt_chainlabel chain, int verbose, +extern int flush_entries4(const ipt_chainlabel chain, int verbose, struct iptc_handle *handle); -extern int for_each_chain(int (*fn)(const ipt_chainlabel, int, struct iptc_handle *), +extern int for_each_chain4(int (*fn)(const ipt_chainlabel, int, struct iptc_handle *), int verbose, int builtinstoo, struct iptc_handle *handle); -extern void print_rule(const struct ipt_entry *e, +extern void print_rule4(const struct ipt_entry *e, struct iptc_handle *handle, const char *chain, int counters); /* kernel revision handling */ diff --git a/include/linux/netfilter/xt_set.h b/include/linux/netfilter/xt_set.h index 3ad31378..4379ce9f 100644 --- a/include/linux/netfilter/xt_set.h +++ b/include/linux/netfilter/xt_set.h @@ -89,7 +89,7 @@ struct xt_set_info_target_v0 { struct xt_set_info_v0 del_set; }; -/* Revision 1: current interface to netfilter/iptables */ +/* Revision 1 match and target */ struct xt_set_info { ip_set_id_t index; @@ -98,13 +98,27 @@ struct xt_set_info { }; /* match and target infos */ -struct xt_set_info_match { +struct xt_set_info_match_v1 { struct xt_set_info match_set; }; -struct xt_set_info_target { +struct xt_set_info_target_v1 { struct xt_set_info add_set; struct xt_set_info del_set; }; +/* Revision 2 target */ + +enum ipset_cmd_flags { + IPSET_FLAG_BIT_EXIST = 0, + IPSET_FLAG_EXIST = (1 << IPSET_FLAG_BIT_EXIST), +}; + +struct xt_set_info_target_v2 { + struct xt_set_info add_set; + struct xt_set_info del_set; + u_int32_t flags; + u_int32_t timeout; +}; + #endif /*_XT_SET_H*/ diff --git a/include/xtables.h.in b/include/xtables.h.in index c3996a09..00be61cf 100644 --- a/include/xtables.h.in +++ b/include/xtables.h.in @@ -455,6 +455,8 @@ extern void xtables_save_string(const char *value); # define _init _INIT # endif extern void init_extensions(void); + extern void init_extensions4(void); + extern void init_extensions6(void); #else # define _init __attribute__((constructor)) _INIT #endif diff --git a/ip6tables-multi.c b/ip6tables-multi.c deleted file mode 100644 index 7e6603f8..00000000 --- a/ip6tables-multi.c +++ /dev/null @@ -1,23 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "xshared.h" - -int ip6tables_main(int argc, char **argv); -int ip6tables_save_main(int argc, char **argv); -int ip6tables_restore_main(int argc, char **argv); - -static const struct subcommand multi6_subcommands[] = { - {"ip6tables", ip6tables_main}, - {"main", ip6tables_main}, - {"ip6tables-save", ip6tables_save_main}, - {"save", ip6tables_save_main}, - {"ip6tables-restore", ip6tables_restore_main}, - {"restore", ip6tables_restore_main}, - {NULL}, -}; - -int main(int argc, char **argv) -{ - return subcmd_main(argc, argv, multi6_subcommands); -} diff --git a/ip6tables-restore.c b/ip6tables-restore.c index 10c3acfc..420bc523 100644 --- a/ip6tables-restore.c +++ b/ip6tables-restore.c @@ -138,6 +138,7 @@ int main(int argc, char *argv[]) } #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) init_extensions(); + init_extensions6(); #endif while ((c = getopt_long(argc, argv, "bcvthnM:", options, NULL)) != -1) { @@ -226,12 +227,12 @@ int main(int argc, char *argv[]) if (noflush == 0) { DEBUGP("Cleaning all chains of table '%s'\n", table); - for_each_chain(flush_entries, verbose, 1, + for_each_chain6(flush_entries6, verbose, 1, handle); DEBUGP("Deleting all user-defined chains " "of table '%s'\n", table); - for_each_chain(delete_chain, verbose, 0, + for_each_chain6(delete_chain6, verbose, 0, handle); } diff --git a/ip6tables-save.c b/ip6tables-save.c index c3b8ec0d..39a33256 100644 --- a/ip6tables-save.c +++ b/ip6tables-save.c @@ -109,7 +109,7 @@ static int do_output(const char *tablename) /* Dump out rules */ e = ip6tc_first_rule(chain, h); while(e) { - print_rule(e, h, chain, show_counters); + print_rule6(e, h, chain, show_counters); e = ip6tc_next_rule(e, h); } } @@ -150,6 +150,7 @@ int main(int argc, char *argv[]) } #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) init_extensions(); + init_extensions6(); #endif while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) { diff --git a/ip6tables-standalone.c b/ip6tables-standalone.c index 8661bd9c..9d8d5a0f 100644 --- a/ip6tables-standalone.c +++ b/ip6tables-standalone.c @@ -60,6 +60,7 @@ main(int argc, char *argv[]) #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) init_extensions(); + init_extensions6(); #endif ret = do_command6(argc, argv, &table, &handle); diff --git a/ip6tables.c b/ip6tables.c index 3beeddf6..967a5f80 100644 --- a/ip6tables.c +++ b/ip6tables.c @@ -124,15 +124,11 @@ static struct option original_opts[] = { {.name = "modprobe", .has_arg = 1, .val = 'M'}, {.name = "set-counters", .has_arg = 1, .val = 'c'}, {.name = "goto", .has_arg = 1, .val = 'g'}, + {.name = "ipv4", .has_arg = 0, .val = '4'}, + {.name = "ipv6", .has_arg = 0, .val = '6'}, {NULL}, }; -/* we need this for ip6tables-restore. ip6tables-restore.c sets line to the - * current line of the input file, in order to give a more precise error - * message. ip6tables itself doesn't need this, so it is initialized to the - * magic number of -1 */ -int line = -1; - void ip6tables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3))); struct xtables_globals ip6tables_globals = { .option_offset = 0, @@ -254,6 +250,8 @@ exit_printhelp(const struct xtables_rule_match *matches) " Change chain name, (moving any references)\n" "Options:\n" +" --ipv4 -4 Error (line is ignored by ip6tables-restore)\n" +" --ipv6 -6 Nothing (line is ignored by iptables-restore)\n" "[!] --proto -p proto protocol: by number or name, eg. `tcp'\n" "[!] --source -s address[/mask][,...]\n" " source specification\n" @@ -859,7 +857,7 @@ check_entry(const ip6t_chainlabel chain, struct ip6t_entry *fw, } int -for_each_chain(int (*fn)(const ip6t_chainlabel, int, struct ip6tc_handle *), +for_each_chain6(int (*fn)(const ip6t_chainlabel, int, struct ip6tc_handle *), int verbose, int builtinstoo, struct ip6tc_handle *handle) { int ret = 1; @@ -895,11 +893,11 @@ for_each_chain(int (*fn)(const ip6t_chainlabel, int, struct ip6tc_handle *), } int -flush_entries(const ip6t_chainlabel chain, int verbose, +flush_entries6(const ip6t_chainlabel chain, int verbose, struct ip6tc_handle *handle) { if (!chain) - return for_each_chain(flush_entries, verbose, 1, handle); + return for_each_chain6(flush_entries6, verbose, 1, handle); if (verbose) fprintf(stdout, "Flushing chain `%s'\n", chain); @@ -911,7 +909,7 @@ zero_entries(const ip6t_chainlabel chain, int verbose, struct ip6tc_handle *handle) { if (!chain) - return for_each_chain(zero_entries, verbose, 1, handle); + return for_each_chain6(zero_entries, verbose, 1, handle); if (verbose) fprintf(stdout, "Zeroing chain `%s'\n", chain); @@ -919,11 +917,11 @@ zero_entries(const ip6t_chainlabel chain, int verbose, } int -delete_chain(const ip6t_chainlabel chain, int verbose, +delete_chain6(const ip6t_chainlabel chain, int verbose, struct ip6tc_handle *handle) { if (!chain) - return for_each_chain(delete_chain, verbose, 0, handle); + return for_each_chain6(delete_chain6, verbose, 0, handle); if (verbose) fprintf(stdout, "Deleting chain `%s'\n", chain); @@ -1083,7 +1081,7 @@ static void print_ip(const char *prefix, const struct in6_addr *ip, /* We want this to be readable, so only print out neccessary fields. * Because that's the kind of world I want to live in. */ -void print_rule(const struct ip6t_entry *e, +void print_rule6(const struct ip6t_entry *e, struct ip6tc_handle *h, const char *chain, int counters) { const struct ip6t_entry_target *t; @@ -1215,7 +1213,7 @@ list_rules(const ip6t_chainlabel chain, int rulenum, int counters, while(e) { num++; if (!rulenum || num == rulenum) - print_rule(e, handle, this, counters); + print_rule6(e, handle, this, counters); e = ip6tc_next_rule(e, handle); } found = 1; @@ -1425,11 +1423,11 @@ int do_command6(int argc, char *argv[], char **table, struct ip6tc_handle **hand cs.jumpto = ""; cs.argv = argv; - /* re-set optind to 0 in case do_command gets called + /* re-set optind to 0 in case do_command6 gets called * a second time */ optind = 0; - /* clear mflags in case do_command gets called a second time + /* clear mflags in case do_command6 gets called a second time * (we clear the global list of all matches for security)*/ for (m = xtables_matches; m; m = m->next) m->mflags = 0; @@ -1445,7 +1443,7 @@ int do_command6(int argc, char *argv[], char **table, struct ip6tc_handle **hand opts = xt_params->orig_opts; while ((cs.c = getopt_long(argc, argv, - "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:bvnt:m:xc:g:", + "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:bvnt:m:xc:g:46", opts, NULL)) != -1) { switch (cs.c) { /* @@ -1762,6 +1760,17 @@ int do_command6(int argc, char *argv[], char **table, struct ip6tc_handle **hand cs.fw6.counters.bcnt = cnt; break; + case '4': + /* This is not the IPv4 iptables */ + if (line != -1) + return 1; /* success: line ignored */ + fprintf(stderr, "This is the IPv6 version of ip6tables.\n"); + exit_tryhelp(2); + + case '6': + /* This is indeed the IPv6 ip6tables */ + break; + case 1: /* non option */ if (optarg[0] == '!' && optarg[1] == '\0') { if (cs.invert) @@ -1953,7 +1962,7 @@ int do_command6(int argc, char *argv[], char **table, struct ip6tc_handle **hand *handle); break; case CMD_FLUSH: - ret = flush_entries(chain, cs.options&OPT_VERBOSE, *handle); + ret = flush_entries6(chain, cs.options&OPT_VERBOSE, *handle); break; case CMD_ZERO: ret = zero_entries(chain, cs.options&OPT_VERBOSE, *handle); @@ -1994,7 +2003,7 @@ int do_command6(int argc, char *argv[], char **table, struct ip6tc_handle **hand ret = ip6tc_create_chain(chain, *handle); break; case CMD_DELETE_CHAIN: - ret = delete_chain(chain, cs.options&OPT_VERBOSE, *handle); + ret = delete_chain6(chain, cs.options&OPT_VERBOSE, *handle); break; case CMD_RENAME_CHAIN: ret = ip6tc_rename_chain(chain, newname, *handle); diff --git a/iptables-multi.c b/iptables-multi.c deleted file mode 100644 index 754b5873..00000000 --- a/iptables-multi.c +++ /dev/null @@ -1,26 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include "xshared.h" - -int iptables_main(int argc, char **argv); -int iptables_save_main(int argc, char **argv); -int iptables_restore_main(int argc, char **argv); -int iptables_xml_main(int argc, char **argv); - -static const struct subcommand multi4_subcommands[] = { - {"iptables", iptables_main}, - {"main", iptables_main}, - {"iptables-save", iptables_save_main}, - {"save", iptables_save_main}, - {"iptables-restore", iptables_restore_main}, - {"restore", iptables_restore_main}, - {"iptables-xml", iptables_xml_main}, - {"xml", iptables_xml_main}, - {NULL}, -}; - -int main(int argc, char **argv) -{ - return subcmd_main(argc, argv, multi4_subcommands); -} diff --git a/iptables-restore.c b/iptables-restore.c index c2cc58c8..26245997 100644 --- a/iptables-restore.c +++ b/iptables-restore.c @@ -141,6 +141,7 @@ main(int argc, char *argv[]) } #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) init_extensions(); + init_extensions4(); #endif while ((c = getopt_long(argc, argv, "bcvthnM:T:", options, NULL)) != -1) { @@ -233,12 +234,12 @@ main(int argc, char *argv[]) if (noflush == 0) { DEBUGP("Cleaning all chains of table '%s'\n", table); - for_each_chain(flush_entries, verbose, 1, + for_each_chain4(flush_entries4, verbose, 1, handle); DEBUGP("Deleting all user-defined chains " "of table '%s'\n", table); - for_each_chain(delete_chain, verbose, 0, + for_each_chain4(delete_chain4, verbose, 0, handle); } @@ -438,13 +439,13 @@ main(int argc, char *argv[]) } } - DEBUGP("calling do_command(%u, argv, &%s, handle):\n", + DEBUGP("calling do_command4(%u, argv, &%s, handle):\n", newargc, curtable); for (a = 0; a < newargc; a++) DEBUGP("argv[%u]: %s\n", a, newargv[a]); - ret = do_command(newargc, newargv, + ret = do_command4(newargc, newargv, &newargv[2], &handle); free_argv(); diff --git a/iptables-save.c b/iptables-save.c index 3e3ec43c..7542bdc0 100644 --- a/iptables-save.c +++ b/iptables-save.c @@ -107,7 +107,7 @@ static int do_output(const char *tablename) /* Dump out rules */ e = iptc_first_rule(chain, h); while(e) { - print_rule(e, h, chain, show_counters); + print_rule4(e, h, chain, show_counters); e = iptc_next_rule(e, h); } } @@ -150,6 +150,7 @@ main(int argc, char *argv[]) } #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) init_extensions(); + init_extensions4(); #endif while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) { diff --git a/iptables-standalone.c b/iptables-standalone.c index 1f60e314..87f1d31a 100644 --- a/iptables-standalone.c +++ b/iptables-standalone.c @@ -60,9 +60,10 @@ main(int argc, char *argv[]) } #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) init_extensions(); + init_extensions4(); #endif - ret = do_command(argc, argv, &table, &handle); + ret = do_command4(argc, argv, &table, &handle); if (ret) { ret = iptc_commit(handle); iptc_free(handle); diff --git a/iptables-xml.c b/iptables-xml.c index 57c7486d..dc3cd4f2 100644 --- a/iptables-xml.c +++ b/iptables-xml.c @@ -844,7 +844,7 @@ main(int argc, char *argv[]) } } - DEBUGP("calling do_command(%u, argv, &%s, handle):\n", + DEBUGP("calling do_command4(%u, argv, &%s, handle):\n", newargc, curTable); for (a = 0; a < newargc; a++) @@ -123,15 +123,11 @@ static struct option original_opts[] = { {.name = "modprobe", .has_arg = 1, .val = 'M'}, {.name = "set-counters", .has_arg = 1, .val = 'c'}, {.name = "goto", .has_arg = 1, .val = 'g'}, + {.name = "ipv4", .has_arg = 0, .val = '4'}, + {.name = "ipv6", .has_arg = 0, .val = '6'}, {NULL}, }; -/* we need this for iptables-restore. iptables-restore.c sets line to the - * current line of the input file, in order to give a more precise error - * message. iptables itself doesn't need this, so it is initialized to the - * magic number of -1 */ -int line = -1; - void iptables_exit_error(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3))); struct xtables_globals iptables_globals = { @@ -267,6 +263,8 @@ exit_printhelp(const struct xtables_rule_match *matches) " Change chain name, (moving any references)\n" "Options:\n" +" --ipv4 -4 Nothing (line is ignored by ip6tables-restore)\n" +" --ipv6 -6 Error (line is ignored by iptables-restore)\n" "[!] --proto -p proto protocol: by number or name, eg. `tcp'\n" "[!] --source -s address[/mask][...]\n" " source specification\n" @@ -862,7 +860,7 @@ check_entry(const ipt_chainlabel chain, struct ipt_entry *fw, } int -for_each_chain(int (*fn)(const ipt_chainlabel, int, struct iptc_handle *), +for_each_chain4(int (*fn)(const ipt_chainlabel, int, struct iptc_handle *), int verbose, int builtinstoo, struct iptc_handle *handle) { int ret = 1; @@ -898,11 +896,11 @@ for_each_chain(int (*fn)(const ipt_chainlabel, int, struct iptc_handle *), } int -flush_entries(const ipt_chainlabel chain, int verbose, +flush_entries4(const ipt_chainlabel chain, int verbose, struct iptc_handle *handle) { if (!chain) - return for_each_chain(flush_entries, verbose, 1, handle); + return for_each_chain4(flush_entries4, verbose, 1, handle); if (verbose) fprintf(stdout, "Flushing chain `%s'\n", chain); @@ -914,7 +912,7 @@ zero_entries(const ipt_chainlabel chain, int verbose, struct iptc_handle *handle) { if (!chain) - return for_each_chain(zero_entries, verbose, 1, handle); + return for_each_chain4(zero_entries, verbose, 1, handle); if (verbose) fprintf(stdout, "Zeroing chain `%s'\n", chain); @@ -922,11 +920,11 @@ zero_entries(const ipt_chainlabel chain, int verbose, } int -delete_chain(const ipt_chainlabel chain, int verbose, +delete_chain4(const ipt_chainlabel chain, int verbose, struct iptc_handle *handle) { if (!chain) - return for_each_chain(delete_chain, verbose, 0, handle); + return for_each_chain4(delete_chain4, verbose, 0, handle); if (verbose) fprintf(stdout, "Deleting chain `%s'\n", chain); @@ -1101,7 +1099,7 @@ static void print_ip(const char *prefix, uint32_t ip, /* We want this to be readable, so only print out neccessary fields. * Because that's the kind of world I want to live in. */ -void print_rule(const struct ipt_entry *e, +void print_rule4(const struct ipt_entry *e, struct iptc_handle *h, const char *chain, int counters) { const struct ipt_entry_target *t; @@ -1224,7 +1222,7 @@ list_rules(const ipt_chainlabel chain, int rulenum, int counters, while(e) { num++; if (!rulenum || num == rulenum) - print_rule(e, handle, this, counters); + print_rule4(e, handle, this, counters); e = iptc_next_rule(e, handle); } found = 1; @@ -1429,7 +1427,7 @@ static void command_match(struct iptables_command_state *cs) xtables_error(OTHER_PROBLEM, "can't alloc memory!"); } -int do_command(int argc, char *argv[], char **table, struct iptc_handle **handle) +int do_command4(int argc, char *argv[], char **table, struct iptc_handle **handle) { struct iptables_command_state cs; struct ipt_entry *e = NULL; @@ -1453,11 +1451,11 @@ int do_command(int argc, char *argv[], char **table, struct iptc_handle **handle cs.jumpto = ""; cs.argv = argv; - /* re-set optind to 0 in case do_command gets called + /* re-set optind to 0 in case do_command4 gets called * a second time */ optind = 0; - /* clear mflags in case do_command gets called a second time + /* clear mflags in case do_command4 gets called a second time * (we clear the global list of all matches for security)*/ for (m = xtables_matches; m; m = m->next) m->mflags = 0; @@ -1473,7 +1471,7 @@ int do_command(int argc, char *argv[], char **table, struct iptc_handle **handle opts = xt_params->orig_opts; while ((cs.c = getopt_long(argc, argv, - "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:", + "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:46", opts, NULL)) != -1) { switch (cs.c) { /* @@ -1788,6 +1786,16 @@ int do_command(int argc, char *argv[], char **table, struct iptc_handle **handle cs.fw.counters.bcnt = cnt; break; + case '4': + /* This is indeed the IPv4 iptables */ + break; + + case '6': + /* This is not the IPv6 ip6tables */ + if (line != -1) + return 1; /* success: line ignored */ + fprintf(stderr, "This is the IPv4 version of iptables.\n"); + exit_tryhelp(2); case 1: /* non option */ if (optarg[0] == '!' && optarg[1] == '\0') { @@ -1989,7 +1997,7 @@ int do_command(int argc, char *argv[], char **table, struct iptc_handle **handle *handle); break; case CMD_FLUSH: - ret = flush_entries(chain, cs.options&OPT_VERBOSE, *handle); + ret = flush_entries4(chain, cs.options&OPT_VERBOSE, *handle); break; case CMD_ZERO: ret = zero_entries(chain, cs.options&OPT_VERBOSE, *handle); @@ -2030,7 +2038,7 @@ int do_command(int argc, char *argv[], char **table, struct iptc_handle **handle ret = iptc_create_chain(chain, *handle); break; case CMD_DELETE_CHAIN: - ret = delete_chain(chain, cs.options&OPT_VERBOSE, *handle); + ret = delete_chain4(chain, cs.options&OPT_VERBOSE, *handle); break; case CMD_RENAME_CHAIN: ret = iptc_rename_chain(chain, newname, *handle); @@ -92,7 +92,7 @@ static bool should_load_proto(struct iptables_command_state *cs) if (find_proto(cs->protocol, XTF_DONT_LOAD, cs->options & OPT_NUMERIC, NULL) == NULL) return true; - return cs->proto_used; + return !cs->proto_used; } struct xtables_match *load_proto(struct iptables_command_state *cs) @@ -29,6 +29,7 @@ struct xtables_target; /** * xtables_afinfo - protocol family dependent information * @kmod: kernel module basename (e.g. "ip_tables") + * @proc_exists: file which exists in procfs when module already loaded * @libprefix: prefix of .so library name (e.g. "libipt_") * @family: nfproto family * @ipproto: used by setsockopt (e.g. IPPROTO_IP) @@ -37,6 +38,7 @@ struct xtables_target; */ struct xtables_afinfo { const char *kmod; + const char *proc_exists; const char *libprefix; uint8_t family; uint8_t ipproto; diff --git a/xtables-multi.c b/xtables-multi.c new file mode 100644 index 00000000..f8d56ce9 --- /dev/null +++ b/xtables-multi.c @@ -0,0 +1,39 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "xshared.h" + +#ifdef ENABLE_IPV4 +#include "iptables-multi.h" +#endif + +#ifdef ENABLE_IPV6 +#include "ip6tables-multi.h" +#endif + +static const struct subcommand multi_subcommands[] = { +#ifdef ENABLE_IPV4 + {"iptables", iptables_main}, + {"main4", iptables_main}, + {"iptables-save", iptables_save_main}, + {"save4", iptables_save_main}, + {"iptables-restore", iptables_restore_main}, + {"restore4", iptables_restore_main}, + {"iptables-xml", iptables_xml_main}, + {"xml4", iptables_xml_main}, +#endif +#ifdef ENABLE_IPV6 + {"ip6tables", ip6tables_main}, + {"main6", ip6tables_main}, + {"ip6tables-save", ip6tables_save_main}, + {"save6", ip6tables_save_main}, + {"ip6tables-restore", ip6tables_restore_main}, + {"restore6", ip6tables_restore_main}, +#endif + {NULL}, +}; + +int main(int argc, char **argv) +{ + return subcmd_main(argc, argv, multi_subcommands); +} @@ -27,9 +27,11 @@ #include <unistd.h> #include <sys/socket.h> #include <sys/stat.h> +#include <sys/statfs.h> #include <sys/types.h> #include <sys/wait.h> #include <arpa/inet.h> +#include <linux/magic.h> /* for PROC_SUPER_MAGIC */ #include <xtables.h> #include <limits.h> /* INT_MAX in ip_tables.h/ip6_tables.h */ @@ -58,6 +60,12 @@ #define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" #endif +/* we need this for ip6?tables-restore. ip6?tables-restore.c sets line to the + * current line of the input file, in order to give a more precise error + * message. ip6?tables itself doesn't need this, so it is initialized to the + * magic number of -1 */ +int line = -1; + void basic_exit_err(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3))); struct xtables_globals *xt_params = NULL; @@ -133,6 +141,7 @@ struct option *xtables_merge_options(struct option *orig_opts, static const struct xtables_afinfo afinfo_ipv4 = { .kmod = "ip_tables", + .proc_exists = "/proc/net/ip_tables_names", .libprefix = "libipt_", .family = NFPROTO_IPV4, .ipproto = IPPROTO_IP, @@ -142,6 +151,7 @@ static const struct xtables_afinfo afinfo_ipv4 = { static const struct xtables_afinfo afinfo_ipv6 = { .kmod = "ip6_tables", + .proc_exists = "/proc/net/ip6_tables_names", .libprefix = "libip6t_", .family = NFPROTO_IPV6, .ipproto = IPPROTO_IPV6, @@ -157,10 +167,18 @@ static const char *xtables_libdir; /* the path to command to load kernel module */ const char *xtables_modprobe_program; -/* Keeping track of external matches and targets: linked lists. */ +/* Keep track of matches/targets pending full registration: linked lists. */ +struct xtables_match *xtables_pending_matches; +struct xtables_target *xtables_pending_targets; + +/* Keep track of fully registered external matches/targets: linked lists. */ struct xtables_match *xtables_matches; struct xtables_target *xtables_targets; +/* Fully register a match/target which was previously partially registered. */ +static void xtables_fully_register_pending_match(struct xtables_match *me); +static void xtables_fully_register_pending_target(struct xtables_target *me); + void xtables_init(void) { xtables_libdir = getenv("XTABLES_LIBDIR"); @@ -355,15 +373,39 @@ int xtables_insmod(const char *modname, const char *modprobe, bool quiet) return -1; } +/* return true if a given file exists within procfs */ +static bool proc_file_exists(const char *filename) +{ + struct stat s; + struct statfs f; + + if (lstat(filename, &s)) + return false; + if (!S_ISREG(s.st_mode)) + return false; + if (statfs(filename, &f)) + return false; + if (f.f_type != PROC_SUPER_MAGIC) + return false; + return true; +} + int xtables_load_ko(const char *modprobe, bool quiet) { static bool loaded = false; - static int ret = -1; + int ret; - if (!loaded) { - ret = xtables_insmod(afinfo->kmod, modprobe, quiet); - loaded = (ret == 0); - } + if (loaded) + return 0; + + if (proc_file_exists(afinfo->proc_exists)) { + loaded = true; + return 0; + }; + + ret = xtables_insmod(afinfo->kmod, modprobe, quiet); + if (ret == 0) + loaded = true; return ret; } @@ -539,6 +581,7 @@ struct xtables_match * xtables_find_match(const char *name, enum xtables_tryload tryload, struct xtables_rule_match **matches) { + struct xtables_match **dptr; struct xtables_match *ptr; const char *icmp6 = "icmp6"; @@ -554,6 +597,18 @@ xtables_find_match(const char *name, enum xtables_tryload tryload, (strcmp(name,"icmp6") == 0) ) name = icmp6; + /* Trigger delayed initialization */ + for (dptr = &xtables_pending_matches; *dptr; ) { + if (strcmp(name, (*dptr)->name) == 0) { + ptr = *dptr; + *dptr = (*dptr)->next; + ptr->next = NULL; + xtables_fully_register_pending_match(ptr); + } else { + dptr = &((*dptr)->next); + } + } + for (ptr = xtables_matches; ptr; ptr = ptr->next) { if (strcmp(name, ptr->name) == 0) { struct xtables_match *clone; @@ -619,6 +674,7 @@ xtables_find_match(const char *name, enum xtables_tryload tryload, struct xtables_target * xtables_find_target(const char *name, enum xtables_tryload tryload) { + struct xtables_target **dptr; struct xtables_target *ptr; /* Standard target? */ @@ -629,6 +685,18 @@ xtables_find_target(const char *name, enum xtables_tryload tryload) || strcmp(name, XTC_LABEL_RETURN) == 0) name = "standard"; + /* Trigger delayed initialization */ + for (dptr = &xtables_pending_targets; *dptr; ) { + if (strcmp(name, (*dptr)->name) == 0) { + ptr = *dptr; + *dptr = (*dptr)->next; + ptr->next = NULL; + xtables_fully_register_pending_target(ptr); + } else { + dptr = &((*dptr)->next); + } + } + for (ptr = xtables_targets; ptr; ptr = ptr->next) { if (strcmp(name, ptr->name) == 0) break; @@ -740,8 +808,6 @@ static void xtables_check_options(const char *name, const struct option *opt) void xtables_register_match(struct xtables_match *me) { - struct xtables_match **i, *old; - if (me->version == NULL) { fprintf(stderr, "%s: match %s<%u> is missing a version\n", xt_params->program_name, me->name, me->revision); @@ -777,6 +843,15 @@ void xtables_register_match(struct xtables_match *me) if (me->family != afinfo->family && me->family != AF_UNSPEC) return; + /* place on linked list of matches pending full registration */ + me->next = xtables_pending_matches; + xtables_pending_matches = me; +} + +static void xtables_fully_register_pending_match(struct xtables_match *me) +{ + struct xtables_match **i, *old; + old = xtables_find_match(me->name, XTF_DURING_LOAD, NULL); if (old) { if (old->revision == me->revision && @@ -830,8 +905,6 @@ void xtables_register_matches(struct xtables_match *match, unsigned int n) void xtables_register_target(struct xtables_target *me) { - struct xtables_target *old; - if (me->version == NULL) { fprintf(stderr, "%s: target %s<%u> is missing a version\n", xt_params->program_name, me->name, me->revision); @@ -867,6 +940,15 @@ void xtables_register_target(struct xtables_target *me) if (me->family != afinfo->family && me->family != AF_UNSPEC) return; + /* place on linked list of targets pending full registration */ + me->next = xtables_pending_targets; + xtables_pending_targets = me; +} + +static void xtables_fully_register_pending_target(struct xtables_target *me) +{ + struct xtables_target *old; + old = xtables_find_target(me->name, XTF_DURING_LOAD); if (old) { struct xtables_target **i; @@ -1323,7 +1405,7 @@ void xtables_ipparse_any(const char *name, struct in_addr **addrpp, const char *xtables_ip6addr_to_numeric(const struct in6_addr *addrp) { - /* 0000:0000:0000:0000:0000:000.000.000.000 + /* 0000:0000:0000:0000:0000:0000:000.000.000.000 * 0000:0000:0000:0000:0000:0000:0000:0000 */ static char buf[50+1]; return inet_ntop(AF_INET6, addrp, buf, sizeof(buf)); |