diff options
Diffstat (limited to 'extensions')
183 files changed, 23716 insertions, 0 deletions
diff --git a/extensions/GNUmakefile.in b/extensions/GNUmakefile.in new file mode 100644 index 0000000..709366a --- /dev/null +++ b/extensions/GNUmakefile.in @@ -0,0 +1,190 @@ +# -*- Makefile -*- + +top_builddir := @top_builddir@ +builddir := @builddir@ +top_srcdir := @top_srcdir@ +srcdir := @srcdir@ +ksourcedir := @ksourcedir@ +prefix := @prefix@ +exec_prefix := @exec_prefix@ +libdir := @libdir@ +libexecdir := @libexecdir@ +xtlibdir := @xtlibdir@ + +CC := @CC@ +CCLD := ${CC} +CFLAGS := @CFLAGS@ +LDFLAGS := @LDFLAGS@ +regular_CFLAGS := @regular_CFLAGS@ +kinclude_CFLAGS := @kinclude_CFLAGS@ + +AM_CFLAGS := ${regular_CFLAGS} -I${top_builddir}/include -I${top_srcdir}/include ${kinclude_CFLAGS} +AM_DEPFLAGS = -Wp,-MMD,$(@D)/.$(@F).d,-MT,$@ + +ifeq (${V},) +AM_LIBTOOL_SILENT = --silent +AM_VERBOSE_CC = @echo " CC " $@; +AM_VERBOSE_CCLD = @echo " CCLD " $@; +AM_VERBOSE_CXX = @echo " CXX " $@; +AM_VERBOSE_CXXLD = @echo " CXXLD " $@; +AM_VERBOSE_AR = @echo " AR " $@; +AM_VERBOSE_GEN = @echo " GEN " $@; +endif + +# +# Wildcard module list +# +pfx_build_mod := $(patsubst ${srcdir}/libxt_%.c,%,$(wildcard ${srcdir}/libxt_*.c)) +@ENABLE_IPV4_TRUE@ pf4_build_mod := $(patsubst ${srcdir}/libipt_%.c,%,$(wildcard ${srcdir}/libipt_*.c)) +@ENABLE_IPV6_TRUE@ pf6_build_mod := $(patsubst ${srcdir}/libip6t_%.c,%,$(wildcard ${srcdir}/libip6t_*.c)) +pfx_build_mod := $(filter-out @blacklist_modules@,${pfx_build_mod}) +pf4_build_mod := $(filter-out @blacklist_modules@,${pf4_build_mod}) +pf6_build_mod := $(filter-out @blacklist_modules@,${pf6_build_mod}) +pfx_objs := $(patsubst %,libxt_%.o,${pfx_build_mod}) +pf4_objs := $(patsubst %,libipt_%.o,${pf4_build_mod}) +pf6_objs := $(patsubst %,libip6t_%.o,${pf6_build_mod}) +pfx_solibs := $(patsubst %,libxt_%.so,${pfx_build_mod}) +pf4_solibs := $(patsubst %,libipt_%.so,${pf4_build_mod}) +pf6_solibs := $(patsubst %,libip6t_%.so,${pf6_build_mod}) + + +# +# Building blocks +# +targets := 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_FALSE@ targets += ${pfx_solibs} ${pf4_solibs} ${pf6_solibs} +@ENABLE_STATIC_FALSE@ targets_install += ${pfx_solibs} ${pf4_solibs} ${pf6_solibs} + +.SECONDARY: + +.PHONY: all install clean distclean FORCE + +all: ${targets} + +install: ${targets_install} + @mkdir -p "${DESTDIR}${xtlibdir}"; + 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; + +distclean: clean + rm -f .*.d .*.dd; + +init%.o: init%.c + ${AM_VERBOSE_CC} ${CC} ${AM_DEPFLAGS} ${AM_CFLAGS} -D_INIT=$*_init ${CFLAGS} -o $@ -c $<; + +-include .*.d + + +# +# Shared libraries +# +lib%.so: lib%.oo + ${AM_VERBOSE_CCLD} ${CCLD} ${AM_LDFLAGS} -shared ${LDFLAGS} -o $@ $<; + +lib%.oo: ${srcdir}/lib%.c + ${AM_VERBOSE_CC} ${CC} ${AM_DEPFLAGS} ${AM_CFLAGS} -D_INIT=lib$*_init -DPIC -fPIC ${CFLAGS} -o $@ -c $<; + + +# +# Static bits +# +# If static building is disabled, libext*.a will still be generated, +# but will be empty. This is good since we can do with less case +# handling code in the Makefiles. +# +lib%.o: ${srcdir}/lib%.c + ${AM_VERBOSE_CC} ${CC} ${AM_DEPFLAGS} ${AM_CFLAGS} -DNO_SHARED_LIBS=1 -D_INIT=lib$*_init ${CFLAGS} -o $@ -c $<; + +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}) + +.initext4.dd: FORCE + @echo "${initext_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 + ${AM_VERBOSE_GEN} + @( \ + echo "" >$@; \ + for i in ${initext_func}; do \ + echo "extern void lib$${i}_init(void);" >>$@; \ + done; \ + echo "void init_extensions(void);" >>$@; \ + echo "void init_extensions(void)" >>$@; \ + echo "{" >>$@; \ + for i in ${initext_func}; do \ + echo " ""lib$${i}_init();" >>$@; \ + done; \ + echo "}" >>$@; \ + ); + +initext6.c: .initext6.dd + ${AM_VERBOSE_GEN} + @( \ + echo "" >$@; \ + 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 "{" >>$@; \ + for i in ${initext6_func}; do \ + echo " ""lib$${i}_init();" >>$@; \ + done; \ + echo "}" >>$@; \ + ); + +# +# Manual pages +# +ex_matches = $(sort $(shell echo $(1) | grep -Eo '\b[a-z0-9]+\b')) +ex_targets = $(sort $(shell echo $(1) | grep -Eo '\b[A-Z0-9]+\b')) +man_run = \ + ${AM_VERBOSE_GEN} \ + for ext in $(1); do \ + f="${srcdir}/libxt_$$ext.man"; \ + cf="${srcdir}/libxt_$$ext.c"; \ + if [ -f "$$f" ] && grep -Eq "$(3)|NFPROTO_UNSPEC" "$$cf"; then \ + echo -e "\t+ $$f" >&2; \ + echo ".SS $$ext"; \ + cat "$$f"; \ + continue; \ + fi; \ + f="${srcdir}/lib$(2)t_$$ext.man"; \ + if [ -f "$$f" ]; then \ + echo -e "\t+ $$f" >&2; \ + echo ".SS $$ext"; \ + cat "$$f"; \ + continue; \ + fi; \ + done >$@; + +matches4.man: .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) + $(call man_run,$(call ex_matches,${pfx_build_mod} ${pf6_build_mod}),ip6,NFPROTO_IPV6) + +targets4.man: .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) + $(call man_run,$(call ex_targets,${pfx_build_mod} ${pf6_build_mod}),ip6,NFPROTO_IPV6) diff --git a/extensions/dscp_helper.c b/extensions/dscp_helper.c new file mode 100644 index 0000000..75b1fec --- /dev/null +++ b/extensions/dscp_helper.c @@ -0,0 +1,79 @@ +/* + * DiffServ classname <-> DiffServ codepoint mapping functions. + * + * The latest list of the mappings can be found at: + * <http://www.iana.org/assignments/dscp-registry> + * + * This code is released under the GNU GPL v2, 1991 + * + * Author: Iain Barnes + */ + +#include <stdio.h> +#include <string.h> +#include <xtables.h> + + +static const struct ds_class +{ + const char *name; + unsigned int dscp; +} ds_classes[] = +{ + { "CS0", 0x00 }, + { "CS1", 0x08 }, + { "CS2", 0x10 }, + { "CS3", 0x18 }, + { "CS4", 0x20 }, + { "CS5", 0x28 }, + { "CS6", 0x30 }, + { "CS7", 0x38 }, + { "BE", 0x00 }, + { "AF11", 0x0a }, + { "AF12", 0x0c }, + { "AF13", 0x0e }, + { "AF21", 0x12 }, + { "AF22", 0x14 }, + { "AF23", 0x16 }, + { "AF31", 0x1a }, + { "AF32", 0x1c }, + { "AF33", 0x1e }, + { "AF41", 0x22 }, + { "AF42", 0x24 }, + { "AF43", 0x26 }, + { "EF", 0x2e } +}; + + + +static unsigned int +class_to_dscp(const char *name) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ds_classes); i++) { + if (!strncasecmp(name, ds_classes[i].name, + strlen(ds_classes[i].name))) + return ds_classes[i].dscp; + } + + xtables_error(PARAMETER_PROBLEM, + "Invalid DSCP value `%s'\n", name); +} + + +#if 0 +static const char * +dscp_to_name(unsigned int dscp) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ds_classes); ++i) + if (dscp == ds_classes[i].dscp) + return ds_classes[i].name; + + xtables_error(PARAMETER_PROBLEM, + "Invalid DSCP value `%d'\n", dscp); +} +#endif + diff --git a/extensions/libip6t_HL.c b/extensions/libip6t_HL.c new file mode 100644 index 0000000..bff0611 --- /dev/null +++ b/extensions/libip6t_HL.c @@ -0,0 +1,157 @@ +/* + * IPv6 Hop Limit Target module + * Maciej Soltysiak <solt@dns.toxicfilms.tv> + * Based on HW's ttl target + * This program is distributed under the terms of GNU GPL + */ + +#include <getopt.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <xtables.h> + +#include <linux/netfilter_ipv6/ip6t_HL.h> + +#define IP6T_HL_USED 1 + +static void HL_help(void) +{ + printf( +"HL target options\n" +" --hl-set value Set HL to <value 0-255>\n" +" --hl-dec value Decrement HL by <value 1-255>\n" +" --hl-inc value Increment HL by <value 1-255>\n"); +} + +static int HL_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct ip6t_HL_info *info = (struct ip6t_HL_info *) (*target)->data; + unsigned int value; + + if (*flags & IP6T_HL_USED) { + xtables_error(PARAMETER_PROBLEM, + "Can't specify HL option twice"); + } + + if (!optarg) + xtables_error(PARAMETER_PROBLEM, + "HL: You must specify a value"); + + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "HL: unexpected `!'"); + + if (!xtables_strtoui(optarg, NULL, &value, 0, UINT8_MAX)) + xtables_error(PARAMETER_PROBLEM, + "HL: Expected value between 0 and 255"); + + switch (c) { + + case '1': + info->mode = IP6T_HL_SET; + break; + + case '2': + if (value == 0) { + xtables_error(PARAMETER_PROBLEM, + "HL: decreasing by 0?"); + } + + info->mode = IP6T_HL_DEC; + break; + + case '3': + if (value == 0) { + xtables_error(PARAMETER_PROBLEM, + "HL: increasing by 0?"); + } + + info->mode = IP6T_HL_INC; + break; + + default: + return 0; + + } + + info->hop_limit = value; + *flags |= IP6T_HL_USED; + + return 1; +} + +static void HL_check(unsigned int flags) +{ + if (!(flags & IP6T_HL_USED)) + xtables_error(PARAMETER_PROBLEM, + "HL: You must specify an action"); +} + +static void HL_save(const void *ip, const struct xt_entry_target *target) +{ + const struct ip6t_HL_info *info = + (struct ip6t_HL_info *) target->data; + + switch (info->mode) { + case IP6T_HL_SET: + printf("--hl-set "); + break; + case IP6T_HL_DEC: + printf("--hl-dec "); + break; + + case IP6T_HL_INC: + printf("--hl-inc "); + break; + } + printf("%u ", info->hop_limit); +} + +static void HL_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct ip6t_HL_info *info = + (struct ip6t_HL_info *) target->data; + + printf("HL "); + switch (info->mode) { + case IP6T_HL_SET: + printf("set to "); + break; + case IP6T_HL_DEC: + printf("decrement by "); + break; + case IP6T_HL_INC: + printf("increment by "); + break; + } + printf("%u ", info->hop_limit); +} + +static const struct option HL_opts[] = { + { "hl-set", 1, NULL, '1' }, + { "hl-dec", 1, NULL, '2' }, + { "hl-inc", 1, NULL, '3' }, + { .name = NULL } +}; + +static struct xtables_target hl_tg6_reg = { + .name = "HL", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct ip6t_HL_info)), + .userspacesize = XT_ALIGN(sizeof(struct ip6t_HL_info)), + .help = HL_help, + .parse = HL_parse, + .final_check = HL_check, + .print = HL_print, + .save = HL_save, + .extra_opts = HL_opts, +}; + +void _init(void) +{ + xtables_register_target(&hl_tg6_reg); +} diff --git a/extensions/libip6t_HL.man b/extensions/libip6t_HL.man new file mode 100644 index 0000000..0f3afb1 --- /dev/null +++ b/extensions/libip6t_HL.man @@ -0,0 +1,17 @@ +This is used to modify the Hop Limit field in IPv6 header. The Hop Limit field +is similar to what is known as TTL value in IPv4. Setting or incrementing the +Hop Limit field can potentially be very dangerous, so it should be avoided at +any cost. This target is only valid in +.B mangle +table. +.PP +.B Don't ever set or increment the value on packets that leave your local network! +.TP +\fB\-\-hl\-set\fP \fIvalue\fP +Set the Hop Limit to `value'. +.TP +\fB\-\-hl\-dec\fP \fIvalue\fP +Decrement the Hop Limit `value' times. +.TP +\fB\-\-hl\-inc\fP \fIvalue\fP +Increment the Hop Limit `value' times. diff --git a/extensions/libip6t_LOG.c b/extensions/libip6t_LOG.c new file mode 100644 index 0000000..423d988 --- /dev/null +++ b/extensions/libip6t_LOG.c @@ -0,0 +1,262 @@ +/* Shared library add-on to ip6tables to add LOG support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> +#include <getopt.h> +#include <xtables.h> +#include <linux/netfilter_ipv6/ip6t_LOG.h> + +#ifndef IP6T_LOG_UID /* Old kernel */ +#define IP6T_LOG_UID 0x08 +#undef IP6T_LOG_MASK +#define IP6T_LOG_MASK 0x0f +#endif + +#define LOG_DEFAULT_LEVEL LOG_WARNING + +static void LOG_help(void) +{ + printf( +"LOG target options:\n" +" --log-level level Level of logging (numeric or see syslog.conf)\n" +" --log-prefix prefix Prefix log messages with this prefix.\n" +" --log-tcp-sequence Log TCP sequence numbers.\n" +" --log-tcp-options Log TCP options.\n" +" --log-ip-options Log IP options.\n" +" --log-uid Log UID owning the local socket.\n"); +} + +static const struct option LOG_opts[] = { + { .name = "log-level", .has_arg = 1, .val = '!' }, + { .name = "log-prefix", .has_arg = 1, .val = '#' }, + { .name = "log-tcp-sequence", .has_arg = 0, .val = '1' }, + { .name = "log-tcp-options", .has_arg = 0, .val = '2' }, + { .name = "log-ip-options", .has_arg = 0, .val = '3' }, + { .name = "log-uid", .has_arg = 0, .val = '4' }, + { .name = NULL } +}; + +static void LOG_init(struct xt_entry_target *t) +{ + struct ip6t_log_info *loginfo = (struct ip6t_log_info *)t->data; + + loginfo->level = LOG_DEFAULT_LEVEL; + +} + +struct ip6t_log_names { + const char *name; + unsigned int level; +}; + +static const struct ip6t_log_names ip6t_log_names[] += { { .name = "alert", .level = LOG_ALERT }, + { .name = "crit", .level = LOG_CRIT }, + { .name = "debug", .level = LOG_DEBUG }, + { .name = "emerg", .level = LOG_EMERG }, + { .name = "error", .level = LOG_ERR }, /* DEPRECATED */ + { .name = "info", .level = LOG_INFO }, + { .name = "notice", .level = LOG_NOTICE }, + { .name = "panic", .level = LOG_EMERG }, /* DEPRECATED */ + { .name = "warning", .level = LOG_WARNING } +}; + +static u_int8_t +parse_level(const char *level) +{ + unsigned int lev = -1; + unsigned int set = 0; + + if (!xtables_strtoui(level, NULL, &lev, 0, 7)) { + unsigned int i = 0; + + for (i = 0; i < ARRAY_SIZE(ip6t_log_names); ++i) + if (strncasecmp(level, ip6t_log_names[i].name, + strlen(level)) == 0) { + if (set++) + xtables_error(PARAMETER_PROBLEM, + "log-level `%s' ambiguous", + level); + lev = ip6t_log_names[i].level; + } + + if (!set) + xtables_error(PARAMETER_PROBLEM, + "log-level `%s' unknown", level); + } + + return lev; +} + +#define IP6T_LOG_OPT_LEVEL 0x01 +#define IP6T_LOG_OPT_PREFIX 0x02 +#define IP6T_LOG_OPT_TCPSEQ 0x04 +#define IP6T_LOG_OPT_TCPOPT 0x08 +#define IP6T_LOG_OPT_IPOPT 0x10 +#define IP6T_LOG_OPT_UID 0x20 + +static int LOG_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct ip6t_log_info *loginfo = (struct ip6t_log_info *)(*target)->data; + + switch (c) { + case '!': + if (*flags & IP6T_LOG_OPT_LEVEL) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --log-level twice"); + + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --log-level"); + + loginfo->level = parse_level(optarg); + *flags |= IP6T_LOG_OPT_LEVEL; + break; + + case '#': + if (*flags & IP6T_LOG_OPT_PREFIX) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --log-prefix twice"); + + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --log-prefix"); + + if (strlen(optarg) > sizeof(loginfo->prefix) - 1) + xtables_error(PARAMETER_PROBLEM, + "Maximum prefix length %u for --log-prefix", + (unsigned int)sizeof(loginfo->prefix) - 1); + + if (strlen(optarg) == 0) + xtables_error(PARAMETER_PROBLEM, + "No prefix specified for --log-prefix"); + + if (strlen(optarg) != strlen(strtok(optarg, "\n"))) + xtables_error(PARAMETER_PROBLEM, + "Newlines not allowed in --log-prefix"); + + strcpy(loginfo->prefix, optarg); + *flags |= IP6T_LOG_OPT_PREFIX; + break; + + case '1': + if (*flags & IP6T_LOG_OPT_TCPSEQ) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --log-tcp-sequence " + "twice"); + + loginfo->logflags |= IP6T_LOG_TCPSEQ; + *flags |= IP6T_LOG_OPT_TCPSEQ; + break; + + case '2': + if (*flags & IP6T_LOG_OPT_TCPOPT) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --log-tcp-options twice"); + + loginfo->logflags |= IP6T_LOG_TCPOPT; + *flags |= IP6T_LOG_OPT_TCPOPT; + break; + + case '3': + if (*flags & IP6T_LOG_OPT_IPOPT) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --log-ip-options twice"); + + loginfo->logflags |= IP6T_LOG_IPOPT; + *flags |= IP6T_LOG_OPT_IPOPT; + break; + + case '4': + if (*flags & IP6T_LOG_OPT_UID) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --log-uid twice"); + + loginfo->logflags |= IP6T_LOG_UID; + *flags |= IP6T_LOG_OPT_UID; + break; + + default: + return 0; + } + + return 1; +} + +static void LOG_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct ip6t_log_info *loginfo + = (const struct ip6t_log_info *)target->data; + unsigned int i = 0; + + printf("LOG "); + if (numeric) + printf("flags %u level %u ", + loginfo->logflags, loginfo->level); + else { + for (i = 0; i < ARRAY_SIZE(ip6t_log_names); ++i) + if (loginfo->level == ip6t_log_names[i].level) { + printf("level %s ", ip6t_log_names[i].name); + break; + } + if (i == ARRAY_SIZE(ip6t_log_names)) + printf("UNKNOWN level %u ", loginfo->level); + if (loginfo->logflags & IP6T_LOG_TCPSEQ) + printf("tcp-sequence "); + if (loginfo->logflags & IP6T_LOG_TCPOPT) + printf("tcp-options "); + if (loginfo->logflags & IP6T_LOG_IPOPT) + printf("ip-options "); + if (loginfo->logflags & IP6T_LOG_UID) + printf("uid "); + if (loginfo->logflags & ~(IP6T_LOG_MASK)) + printf("unknown-flags "); + } + + if (strcmp(loginfo->prefix, "") != 0) + printf("prefix `%s' ", loginfo->prefix); +} + +static void LOG_save(const void *ip, const struct xt_entry_target *target) +{ + const struct ip6t_log_info *loginfo + = (const struct ip6t_log_info *)target->data; + + if (strcmp(loginfo->prefix, "") != 0) + printf("--log-prefix \"%s\" ", loginfo->prefix); + + if (loginfo->level != LOG_DEFAULT_LEVEL) + printf("--log-level %d ", loginfo->level); + + if (loginfo->logflags & IP6T_LOG_TCPSEQ) + printf("--log-tcp-sequence "); + if (loginfo->logflags & IP6T_LOG_TCPOPT) + printf("--log-tcp-options "); + if (loginfo->logflags & IP6T_LOG_IPOPT) + printf("--log-ip-options "); + if (loginfo->logflags & IP6T_LOG_UID) + printf("--log-uid "); +} + +static struct xtables_target log_tg6_reg = { + .name = "LOG", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct ip6t_log_info)), + .userspacesize = XT_ALIGN(sizeof(struct ip6t_log_info)), + .help = LOG_help, + .init = LOG_init, + .parse = LOG_parse, + .print = LOG_print, + .save = LOG_save, + .extra_opts = LOG_opts, +}; + +void _init(void) +{ + xtables_register_target(&log_tg6_reg); +} diff --git a/extensions/libip6t_LOG.man b/extensions/libip6t_LOG.man new file mode 100644 index 0000000..b7803fe --- /dev/null +++ b/extensions/libip6t_LOG.man @@ -0,0 +1,31 @@ +Turn on kernel logging of matching packets. When this option is set +for a rule, the Linux kernel will print some information on all +matching packets (like most IPv6 IPv6-header fields) via the kernel log +(where it can be read with +.I dmesg +or +.IR syslogd (8)). +This is a "non-terminating target", i.e. rule traversal continues at +the next rule. So if you want to LOG the packets you refuse, use two +separate rules with the same matching criteria, first using target LOG +then DROP (or REJECT). +.TP +\fB\-\-log\-level\fP \fIlevel\fP +Level of logging (numeric or see \fIsyslog.conf\fP(5)). +.TP +\fB\-\-log\-prefix\fP \fIprefix\fP +Prefix log messages with the specified prefix; up to 29 letters long, +and useful for distinguishing messages in the logs. +.TP +\fB\-\-log\-tcp\-sequence\fP +Log TCP sequence numbers. This is a security risk if the log is +readable by users. +.TP +\fB\-\-log\-tcp\-options\fP +Log options from the TCP packet header. +.TP +\fB\-\-log\-ip\-options\fP +Log options from the IPv6 packet header. +.TP +\fB\-\-log\-uid\fP +Log the userid of the process which generated the packet. diff --git a/extensions/libip6t_REJECT.c b/extensions/libip6t_REJECT.c new file mode 100644 index 0000000..b8195d7 --- /dev/null +++ b/extensions/libip6t_REJECT.c @@ -0,0 +1,146 @@ +/* Shared library add-on to ip6tables to add customized REJECT support. + * + * (C) 2000 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> + * + * ported to IPv6 by Harald Welte <laforge@gnumonks.org> + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <linux/netfilter_ipv6/ip6t_REJECT.h> + +struct reject_names { + const char *name; + const char *alias; + enum ip6t_reject_with with; + const char *desc; +}; + +static const struct reject_names reject_table[] = { + {"icmp6-no-route", "no-route", + IP6T_ICMP6_NO_ROUTE, "ICMPv6 no route"}, + {"icmp6-adm-prohibited", "adm-prohibited", + IP6T_ICMP6_ADM_PROHIBITED, "ICMPv6 administratively prohibited"}, +#if 0 + {"icmp6-not-neighbor", "not-neighbor"}, + IP6T_ICMP6_NOT_NEIGHBOR, "ICMPv6 not a neighbor"}, +#endif + {"icmp6-addr-unreachable", "addr-unreach", + IP6T_ICMP6_ADDR_UNREACH, "ICMPv6 address unreachable"}, + {"icmp6-port-unreachable", "port-unreach", + IP6T_ICMP6_PORT_UNREACH, "ICMPv6 port unreachable"}, + {"tcp-reset", "tcp-reset", + IP6T_TCP_RESET, "TCP RST packet"} +}; + +static void +print_reject_types(void) +{ + unsigned int i; + + printf("Valid reject types:\n"); + + for (i = 0; i < ARRAY_SIZE(reject_table); ++i) { + printf(" %-25s\t%s\n", reject_table[i].name, reject_table[i].desc); + printf(" %-25s\talias\n", reject_table[i].alias); + } + printf("\n"); +} + +static void REJECT_help(void) +{ + printf( +"REJECT target options:\n" +"--reject-with type drop input packet and send back\n" +" a reply packet according to type:\n"); + + print_reject_types(); +} + +static const struct option REJECT_opts[] = { + { "reject-with", 1, NULL, '1' }, + { .name = NULL } +}; + +static void REJECT_init(struct xt_entry_target *t) +{ + struct ip6t_reject_info *reject = (struct ip6t_reject_info *)t->data; + + /* default */ + reject->with = IP6T_ICMP6_PORT_UNREACH; + +} + +static int REJECT_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct ip6t_reject_info *reject = + (struct ip6t_reject_info *)(*target)->data; + unsigned int i; + + switch(c) { + case '1': + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --reject-with"); + for (i = 0; i < ARRAY_SIZE(reject_table); ++i) + if ((strncasecmp(reject_table[i].name, optarg, strlen(optarg)) == 0) + || (strncasecmp(reject_table[i].alias, optarg, strlen(optarg)) == 0)) { + reject->with = reject_table[i].with; + return 1; + } + xtables_error(PARAMETER_PROBLEM, "unknown reject type \"%s\"", optarg); + default: + /* Fall through */ + break; + } + return 0; +} + +static void REJECT_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct ip6t_reject_info *reject + = (const struct ip6t_reject_info *)target->data; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(reject_table); ++i) + if (reject_table[i].with == reject->with) + break; + printf("reject-with %s ", reject_table[i].name); +} + +static void REJECT_save(const void *ip, const struct xt_entry_target *target) +{ + const struct ip6t_reject_info *reject + = (const struct ip6t_reject_info *)target->data; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(reject_table); ++i) + if (reject_table[i].with == reject->with) + break; + + printf("--reject-with %s ", reject_table[i].name); +} + +static struct xtables_target reject_tg6_reg = { + .name = "REJECT", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct ip6t_reject_info)), + .userspacesize = XT_ALIGN(sizeof(struct ip6t_reject_info)), + .help = REJECT_help, + .init = REJECT_init, + .parse = REJECT_parse, + .print = REJECT_print, + .save = REJECT_save, + .extra_opts = REJECT_opts, +}; + +void _init(void) +{ + xtables_register_target(&reject_tg6_reg); +} diff --git a/extensions/libip6t_REJECT.man b/extensions/libip6t_REJECT.man new file mode 100644 index 0000000..2d09e05 --- /dev/null +++ b/extensions/libip6t_REJECT.man @@ -0,0 +1,33 @@ +This is used to send back an error packet in response to the matched +packet: otherwise it is equivalent to +.B DROP +so it is a terminating TARGET, ending rule traversal. +This target is only valid in the +.BR INPUT , +.B FORWARD +and +.B OUTPUT +chains, and user-defined chains which are only called from those +chains. The following option controls the nature of the error packet +returned: +.TP +\fB\-\-reject\-with\fP \fItype\fP +The type given can be +\fBicmp6\-no\-route\fP, +\fBno\-route\fP, +\fBicmp6\-adm\-prohibited\fP, +\fBadm\-prohibited\fP, +\fBicmp6\-addr\-unreachable\fP, +\fBaddr\-unreach\fP, +\fBicmp6\-port\-unreachable\fP or +\fBport\-unreach\fP +which return the appropriate ICMPv6 error message (\fBport\-unreach\fP is +the default). Finally, the option +\fBtcp\-reset\fP +can be used on rules which only match the TCP protocol: this causes a +TCP RST packet to be sent back. This is mainly useful for blocking +.I ident +(113/tcp) probes which frequently occur when sending mail to broken mail +hosts (which won't accept your mail otherwise). +\fBtcp\-reset\fP +can only be used with kernel versions 2.6.14 or later. diff --git a/extensions/libip6t_ah.c b/extensions/libip6t_ah.c new file mode 100644 index 0000000..285704c --- /dev/null +++ b/extensions/libip6t_ah.c @@ -0,0 +1,207 @@ +/* Shared library add-on to ip6tables to add AH support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <errno.h> +#include <xtables.h> +#include <linux/netfilter_ipv6/ip6t_ah.h> + +static void ah_help(void) +{ + printf( +"ah match options:\n" +"[!] --ahspi spi[:spi] match spi (range)\n" +"[!] --ahlen length total length of this header\n" +" --ahres check the reserved filed, too\n"); +} + +static const struct option ah_opts[] = { + { .name = "ahspi", .has_arg = 1, .val = '1' }, + { .name = "ahlen", .has_arg = 1, .val = '2' }, + { .name = "ahres", .has_arg = 0, .val = '3' }, + { .name = NULL } +}; + +static u_int32_t +parse_ah_spi(const char *spistr, const char *typestr) +{ + unsigned long int spi; + char* ep; + + spi = strtoul(spistr, &ep, 0); + + if ( spistr == ep ) + xtables_error(PARAMETER_PROBLEM, + "AH no valid digits in %s `%s'", typestr, spistr); + + if ( spi == ULONG_MAX && errno == ERANGE ) + xtables_error(PARAMETER_PROBLEM, + "%s `%s' specified too big: would overflow", + typestr, spistr); + + if ( *spistr != '\0' && *ep != '\0' ) + xtables_error(PARAMETER_PROBLEM, + "AH error parsing %s `%s'", typestr, spistr); + + return spi; +} + +static void +parse_ah_spis(const char *spistring, u_int32_t *spis) +{ + char *buffer; + char *cp; + + buffer = strdup(spistring); + if ((cp = strchr(buffer, ':')) == NULL) + spis[0] = spis[1] = parse_ah_spi(buffer, "spi"); + else { + *cp = '\0'; + cp++; + + spis[0] = buffer[0] ? parse_ah_spi(buffer, "spi") : 0; + spis[1] = cp[0] ? parse_ah_spi(cp, "spi") : 0xFFFFFFFF; + } + free(buffer); +} + +static void ah_init(struct xt_entry_match *m) +{ + struct ip6t_ah *ahinfo = (struct ip6t_ah *)m->data; + + ahinfo->spis[1] = 0xFFFFFFFF; + ahinfo->hdrlen = 0; + ahinfo->hdrres = 0; +} + +static int ah_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ip6t_ah *ahinfo = (struct ip6t_ah *)(*match)->data; + + switch (c) { + case '1': + if (*flags & IP6T_AH_SPI) + xtables_error(PARAMETER_PROBLEM, + "Only one `--ahspi' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_ah_spis(optarg, ahinfo->spis); + if (invert) + ahinfo->invflags |= IP6T_AH_INV_SPI; + *flags |= IP6T_AH_SPI; + break; + case '2': + if (*flags & IP6T_AH_LEN) + xtables_error(PARAMETER_PROBLEM, + "Only one `--ahlen' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + ahinfo->hdrlen = parse_ah_spi(optarg, "length"); + if (invert) + ahinfo->invflags |= IP6T_AH_INV_LEN; + *flags |= IP6T_AH_LEN; + break; + case '3': + if (*flags & IP6T_AH_RES) + xtables_error(PARAMETER_PROBLEM, + "Only one `--ahres' allowed"); + ahinfo->hdrres = 1; + *flags |= IP6T_AH_RES; + break; + default: + return 0; + } + + return 1; +} + +static void +print_spis(const char *name, u_int32_t min, u_int32_t max, + int invert) +{ + const char *inv = invert ? "!" : ""; + + if (min != 0 || max != 0xFFFFFFFF || invert) { + if (min == max) + printf("%s:%s%u ", name, inv, min); + else + printf("%ss:%s%u:%u ", name, inv, min, max); + } +} + +static void +print_len(const char *name, u_int32_t len, int invert) +{ + const char *inv = invert ? "!" : ""; + + if (len != 0 || invert) + printf("%s:%s%u ", name, inv, len); +} + +static void ah_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ip6t_ah *ah = (struct ip6t_ah *)match->data; + + printf("ah "); + print_spis("spi", ah->spis[0], ah->spis[1], + ah->invflags & IP6T_AH_INV_SPI); + print_len("length", ah->hdrlen, + ah->invflags & IP6T_AH_INV_LEN); + + if (ah->hdrres) + printf("reserved "); + + if (ah->invflags & ~IP6T_AH_INV_MASK) + printf("Unknown invflags: 0x%X ", + ah->invflags & ~IP6T_AH_INV_MASK); +} + +static void ah_save(const void *ip, const struct xt_entry_match *match) +{ + const struct ip6t_ah *ahinfo = (struct ip6t_ah *)match->data; + + if (!(ahinfo->spis[0] == 0 + && ahinfo->spis[1] == 0xFFFFFFFF)) { + printf("%s--ahspi ", + (ahinfo->invflags & IP6T_AH_INV_SPI) ? "! " : ""); + if (ahinfo->spis[0] + != ahinfo->spis[1]) + printf("%u:%u ", + ahinfo->spis[0], + ahinfo->spis[1]); + else + printf("%u ", + ahinfo->spis[0]); + } + + if (ahinfo->hdrlen != 0 || (ahinfo->invflags & IP6T_AH_INV_LEN) ) { + printf("%s--ahlen %u ", + (ahinfo->invflags & IP6T_AH_INV_LEN) ? "! " : "", + ahinfo->hdrlen); + } + + if (ahinfo->hdrres != 0 ) + printf("--ahres "); +} + +static struct xtables_match ah_mt6_reg = { + .name = "ah", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct ip6t_ah)), + .userspacesize = XT_ALIGN(sizeof(struct ip6t_ah)), + .help = ah_help, + .init = ah_init, + .parse = ah_parse, + .print = ah_print, + .save = ah_save, + .extra_opts = ah_opts, +}; + +void +_init(void) +{ + xtables_register_match(&ah_mt6_reg); +} diff --git a/extensions/libip6t_ah.man b/extensions/libip6t_ah.man new file mode 100644 index 0000000..9c24dcf --- /dev/null +++ b/extensions/libip6t_ah.man @@ -0,0 +1,10 @@ +This module matches the parameters in Authentication header of IPsec packets. +.TP +[\fB!\fP] \fB\-\-ahspi\fP \fIspi\fP[\fB:\fP\fIspi\fP] +Matches SPI. +.TP +[\fB!\fP] \fB\-\-ahlen\fP \fIlength\fP +Total length of this header in octets. +.TP +\fB\-\-ahres\fP +Matches if the reserved field is filled with zero. diff --git a/extensions/libip6t_dst.c b/extensions/libip6t_dst.c new file mode 100644 index 0000000..72df6ad --- /dev/null +++ b/extensions/libip6t_dst.c @@ -0,0 +1,241 @@ +/* Shared library add-on to ip6tables to add Dst header support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <errno.h> +#include <xtables.h> +#include <linux/netfilter_ipv6/ip6t_opts.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +static void dst_help(void) +{ + printf( +"dst match options:\n" +"[!] --dst-len length total length of this header\n" +" --dst-opts TYPE[:LEN][,TYPE[:LEN]...]\n" +" Options and its length (list, max: %d)\n", +IP6T_OPTS_OPTSNR); +} + +static const struct option dst_opts[] = { + { .name = "dst-len", .has_arg = 1, .val = '1' }, + { .name = "dst-opts", .has_arg = 1, .val = '2' }, + { .name = "dst-not-strict", .has_arg = 1, .val = '3' }, + { .name = NULL } +}; + +static u_int32_t +parse_opts_num(const char *idstr, const char *typestr) +{ + unsigned long int id; + char* ep; + + id = strtoul(idstr, &ep, 0); + + if ( idstr == ep ) { + xtables_error(PARAMETER_PROBLEM, + "dst: no valid digits in %s `%s'", typestr, idstr); + } + if ( id == ULONG_MAX && errno == ERANGE ) { + xtables_error(PARAMETER_PROBLEM, + "%s `%s' specified too big: would overflow", + typestr, idstr); + } + if ( *idstr != '\0' && *ep != '\0' ) { + xtables_error(PARAMETER_PROBLEM, + "dst: error parsing %s `%s'", typestr, idstr); + } + return id; +} + +static int +parse_options(const char *optsstr, u_int16_t *opts) +{ + char *buffer, *cp, *next, *range; + unsigned int i; + + buffer = strdup(optsstr); + if (!buffer) + xtables_error(OTHER_PROBLEM, "strdup failed"); + + for (cp = buffer, i = 0; cp && i < IP6T_OPTS_OPTSNR; cp = next, i++) + { + next = strchr(cp, ','); + + if (next) + *next++='\0'; + + range = strchr(cp, ':'); + + if (range) { + if (i == IP6T_OPTS_OPTSNR-1) + xtables_error(PARAMETER_PROBLEM, + "too many ports specified"); + *range++ = '\0'; + } + + opts[i] = (parse_opts_num(cp, "opt") & 0xFF) << 8; + if (range) { + if (opts[i] == 0) + xtables_error(PARAMETER_PROBLEM, + "PAD0 hasn't got length"); + opts[i] |= parse_opts_num(range, "length") & 0xFF; + } else + opts[i] |= (0x00FF); + +#ifdef DEBUG + printf("opts str: %s %s\n", cp, range); + printf("opts opt: %04X\n", opts[i]); +#endif + } + + if (cp) + xtables_error(PARAMETER_PROBLEM, "too many addresses specified"); + + free(buffer); + +#ifdef DEBUG + printf("addr nr: %d\n", i); +#endif + + return i; +} + +static void dst_init(struct xt_entry_match *m) +{ + struct ip6t_opts *optinfo = (struct ip6t_opts *)m->data; + + optinfo->hdrlen = 0; + optinfo->flags = 0; + optinfo->invflags = 0; + optinfo->optsnr = 0; +} + +static int dst_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ip6t_opts *optinfo = (struct ip6t_opts *)(*match)->data; + + switch (c) { + case '1': + if (*flags & IP6T_OPTS_LEN) + xtables_error(PARAMETER_PROBLEM, + "Only one `--dst-len' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + optinfo->hdrlen = parse_opts_num(optarg, "length"); + if (invert) + optinfo->invflags |= IP6T_OPTS_INV_LEN; + optinfo->flags |= IP6T_OPTS_LEN; + *flags |= IP6T_OPTS_LEN; + break; + case '2': + if (*flags & IP6T_OPTS_OPTS) + xtables_error(PARAMETER_PROBLEM, + "Only one `--dst-opts' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) + xtables_error(PARAMETER_PROBLEM, + " '!' not allowed with `--dst-opts'"); + optinfo->optsnr = parse_options(optarg, optinfo->opts); + optinfo->flags |= IP6T_OPTS_OPTS; + *flags |= IP6T_OPTS_OPTS; + break; + case '3': + if (*flags & IP6T_OPTS_NSTRICT) + xtables_error(PARAMETER_PROBLEM, + "Only one `--dst-not-strict' allowed"); + if ( !(*flags & IP6T_OPTS_OPTS) ) + xtables_error(PARAMETER_PROBLEM, + "`--dst-opts ...' required before " + "`--dst-not-strict'"); + optinfo->flags |= IP6T_OPTS_NSTRICT; + *flags |= IP6T_OPTS_NSTRICT; + break; + default: + return 0; + } + + return 1; +} + +static void +print_options(unsigned int optsnr, u_int16_t *optsp) +{ + unsigned int i; + + for(i = 0; i < optsnr; i++) { + printf("%d", (optsp[i] & 0xFF00) >> 8); + + if ((optsp[i] & 0x00FF) != 0x00FF) + printf(":%d", (optsp[i] & 0x00FF)); + + printf("%c", (i != optsnr - 1) ? ',' : ' '); + } +} + +static void dst_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data; + + printf("dst "); + if (optinfo->flags & IP6T_OPTS_LEN) + printf("length:%s%u ", + optinfo->invflags & IP6T_OPTS_INV_LEN ? "!" : "", + optinfo->hdrlen); + + if (optinfo->flags & IP6T_OPTS_OPTS) + printf("opts "); + + print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts); + + if (optinfo->flags & IP6T_OPTS_NSTRICT) + printf("not-strict "); + + if (optinfo->invflags & ~IP6T_OPTS_INV_MASK) + printf("Unknown invflags: 0x%X ", + optinfo->invflags & ~IP6T_OPTS_INV_MASK); +} + +static void dst_save(const void *ip, const struct xt_entry_match *match) +{ + const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data; + + if (optinfo->flags & IP6T_OPTS_LEN) { + printf("%s--dst-len %u ", + (optinfo->invflags & IP6T_OPTS_INV_LEN) ? "! " : "", + optinfo->hdrlen); + } + + if (optinfo->flags & IP6T_OPTS_OPTS) + printf("--dst-opts "); + + print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts); + + if (optinfo->flags & IP6T_OPTS_NSTRICT) + printf("--dst-not-strict "); +} + +static struct xtables_match dst_mt6_reg = { + .name = "dst", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct ip6t_opts)), + .userspacesize = XT_ALIGN(sizeof(struct ip6t_opts)), + .help = dst_help, + .init = dst_init, + .parse = dst_parse, + .print = dst_print, + .save = dst_save, + .extra_opts = dst_opts, +}; + +void +_init(void) +{ + xtables_register_match(&dst_mt6_reg); +} diff --git a/extensions/libip6t_dst.man b/extensions/libip6t_dst.man new file mode 100644 index 0000000..bfbb501 --- /dev/null +++ b/extensions/libip6t_dst.man @@ -0,0 +1,7 @@ +This module matches the parameters in Destination Options header +.TP +[\fB!\fP] \fB\-\-dst\-len\fP \fIlength\fP +Total length of this header in octets. +.TP +\fB\-\-dst\-opts\fP \fItype\fP[\fB:\fP\fIlength\fP][\fB,\fP\fItype\fP[\fB:\fP\fIlength\fP]...] +numeric type of option and the length of the option data in octets. diff --git a/extensions/libip6t_eui64.c b/extensions/libip6t_eui64.c new file mode 100644 index 0000000..607bf86 --- /dev/null +++ b/extensions/libip6t_eui64.c @@ -0,0 +1,15 @@ +/* Shared library add-on to ip6tables to add EUI64 address checking support. */ +#include <xtables.h> + +static struct xtables_match eui64_mt6_reg = { + .name = "eui64", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(int)), + .userspacesize = XT_ALIGN(sizeof(int)), +}; + +void _init(void) +{ + xtables_register_match(&eui64_mt6_reg); +} diff --git a/extensions/libip6t_eui64.man b/extensions/libip6t_eui64.man new file mode 100644 index 0000000..cd80b98 --- /dev/null +++ b/extensions/libip6t_eui64.man @@ -0,0 +1,10 @@ +This module matches the EUI-64 part of a stateless autoconfigured IPv6 address. +It compares the EUI-64 derived from the source MAC address in Ethernet frame +with the lower 64 bits of the IPv6 source address. But "Universal/Local" +bit is not compared. This module doesn't match other link layer frame, and +is only valid in the +.BR PREROUTING , +.BR INPUT +and +.BR FORWARD +chains. diff --git a/extensions/libip6t_frag.c b/extensions/libip6t_frag.c new file mode 100644 index 0000000..5a280cc --- /dev/null +++ b/extensions/libip6t_frag.c @@ -0,0 +1,252 @@ +/* Shared library add-on to ip6tables to add Fragmentation header support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <errno.h> +#include <xtables.h> +#include <linux/netfilter_ipv6/ip6t_frag.h> + +static void frag_help(void) +{ + printf( +"frag match options:\n" +"[!] --fragid id[:id] match the id (range)\n" +"[!] --fraglen length total length of this header\n" +" --fragres check the reserved filed, too\n" +" --fragfirst matches on the first fragment\n" +" [--fragmore|--fraglast] there are more fragments or this\n" +" is the last one\n"); +} + +static const struct option frag_opts[] = { + { .name = "fragid", .has_arg = 1, .val = '1' }, + { .name = "fraglen", .has_arg = 1, .val = '2' }, + { .name = "fragres", .has_arg = 0, .val = '3' }, + { .name = "fragfirst", .has_arg = 0, .val = '4' }, + { .name = "fragmore", .has_arg = 0, .val = '5' }, + { .name = "fraglast", .has_arg = 0, .val = '6' }, + { .name = NULL } +}; + +static u_int32_t +parse_frag_id(const char *idstr, const char *typestr) +{ + unsigned long int id; + char* ep; + + id = strtoul(idstr, &ep, 0); + + if ( idstr == ep ) { + xtables_error(PARAMETER_PROBLEM, + "FRAG no valid digits in %s `%s'", typestr, idstr); + } + if ( id == ULONG_MAX && errno == ERANGE ) { + xtables_error(PARAMETER_PROBLEM, + "%s `%s' specified too big: would overflow", + typestr, idstr); + } + if ( *idstr != '\0' && *ep != '\0' ) { + xtables_error(PARAMETER_PROBLEM, + "FRAG error parsing %s `%s'", typestr, idstr); + } + return id; +} + +static void +parse_frag_ids(const char *idstring, u_int32_t *ids) +{ + char *buffer; + char *cp; + + buffer = strdup(idstring); + if ((cp = strchr(buffer, ':')) == NULL) + ids[0] = ids[1] = parse_frag_id(buffer,"id"); + else { + *cp = '\0'; + cp++; + + ids[0] = buffer[0] ? parse_frag_id(buffer,"id") : 0; + ids[1] = cp[0] ? parse_frag_id(cp,"id") : 0xFFFFFFFF; + } + free(buffer); +} + +static void frag_init(struct xt_entry_match *m) +{ + struct ip6t_frag *fraginfo = (struct ip6t_frag *)m->data; + + fraginfo->ids[0] = 0x0L; + fraginfo->ids[1] = 0xFFFFFFFF; + fraginfo->hdrlen = 0; + fraginfo->flags = 0; + fraginfo->invflags = 0; +} + +static int frag_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ip6t_frag *fraginfo = (struct ip6t_frag *)(*match)->data; + + switch (c) { + case '1': + if (*flags & IP6T_FRAG_IDS) + xtables_error(PARAMETER_PROBLEM, + "Only one `--fragid' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_frag_ids(optarg, fraginfo->ids); + if (invert) + fraginfo->invflags |= IP6T_FRAG_INV_IDS; + fraginfo->flags |= IP6T_FRAG_IDS; + *flags |= IP6T_FRAG_IDS; + break; + case '2': + if (*flags & IP6T_FRAG_LEN) + xtables_error(PARAMETER_PROBLEM, + "Only one `--fraglen' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + fraginfo->hdrlen = parse_frag_id(optarg, "length"); + if (invert) + fraginfo->invflags |= IP6T_FRAG_INV_LEN; + fraginfo->flags |= IP6T_FRAG_LEN; + *flags |= IP6T_FRAG_LEN; + break; + case '3': + if (*flags & IP6T_FRAG_RES) + xtables_error(PARAMETER_PROBLEM, + "Only one `--fragres' allowed"); + fraginfo->flags |= IP6T_FRAG_RES; + *flags |= IP6T_FRAG_RES; + break; + case '4': + if (*flags & IP6T_FRAG_FST) + xtables_error(PARAMETER_PROBLEM, + "Only one `--fragfirst' allowed"); + fraginfo->flags |= IP6T_FRAG_FST; + *flags |= IP6T_FRAG_FST; + break; + case '5': + if (*flags & (IP6T_FRAG_MF|IP6T_FRAG_NMF)) + xtables_error(PARAMETER_PROBLEM, + "Only one `--fragmore' or `--fraglast' allowed"); + fraginfo->flags |= IP6T_FRAG_MF; + *flags |= IP6T_FRAG_MF; + break; + case '6': + if (*flags & (IP6T_FRAG_MF|IP6T_FRAG_NMF)) + xtables_error(PARAMETER_PROBLEM, + "Only one `--fragmore' or `--fraglast' allowed"); + fraginfo->flags |= IP6T_FRAG_NMF; + *flags |= IP6T_FRAG_NMF; + break; + default: + return 0; + } + + return 1; +} + +static void +print_ids(const char *name, u_int32_t min, u_int32_t max, + int invert) +{ + const char *inv = invert ? "!" : ""; + + if (min != 0 || max != 0xFFFFFFFF || invert) { + printf("%s", name); + if (min == max) + printf(":%s%u ", inv, min); + else + printf("s:%s%u:%u ", inv, min, max); + } +} + +static void frag_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ip6t_frag *frag = (struct ip6t_frag *)match->data; + + printf("frag "); + print_ids("id", frag->ids[0], frag->ids[1], + frag->invflags & IP6T_FRAG_INV_IDS); + + if (frag->flags & IP6T_FRAG_LEN) { + printf("length:%s%u ", + frag->invflags & IP6T_FRAG_INV_LEN ? "!" : "", + frag->hdrlen); + } + + if (frag->flags & IP6T_FRAG_RES) + printf("reserved "); + + if (frag->flags & IP6T_FRAG_FST) + printf("first "); + + if (frag->flags & IP6T_FRAG_MF) + printf("more "); + + if (frag->flags & IP6T_FRAG_NMF) + printf("last "); + + if (frag->invflags & ~IP6T_FRAG_INV_MASK) + printf("Unknown invflags: 0x%X ", + frag->invflags & ~IP6T_FRAG_INV_MASK); +} + +static void frag_save(const void *ip, const struct xt_entry_match *match) +{ + const struct ip6t_frag *fraginfo = (struct ip6t_frag *)match->data; + + if (!(fraginfo->ids[0] == 0 + && fraginfo->ids[1] == 0xFFFFFFFF)) { + printf("%s--fragid ", + (fraginfo->invflags & IP6T_FRAG_INV_IDS) ? "! " : ""); + if (fraginfo->ids[0] + != fraginfo->ids[1]) + printf("%u:%u ", + fraginfo->ids[0], + fraginfo->ids[1]); + else + printf("%u ", + fraginfo->ids[0]); + } + + if (fraginfo->flags & IP6T_FRAG_LEN) { + printf("%s--fraglen %u ", + (fraginfo->invflags & IP6T_FRAG_INV_LEN) ? "! " : "", + fraginfo->hdrlen); + } + + if (fraginfo->flags & IP6T_FRAG_RES) + printf("--fragres "); + + if (fraginfo->flags & IP6T_FRAG_FST) + printf("--fragfirst "); + + if (fraginfo->flags & IP6T_FRAG_MF) + printf("--fragmore "); + + if (fraginfo->flags & IP6T_FRAG_NMF) + printf("--fraglast "); +} + +static struct xtables_match frag_mt6_reg = { + .name = "frag", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct ip6t_frag)), + .userspacesize = XT_ALIGN(sizeof(struct ip6t_frag)), + .help = frag_help, + .init = frag_init, + .parse = frag_parse, + .print = frag_print, + .save = frag_save, + .extra_opts = frag_opts, +}; + +void +_init(void) +{ + xtables_register_match(&frag_mt6_reg); +} diff --git a/extensions/libip6t_frag.man b/extensions/libip6t_frag.man new file mode 100644 index 0000000..7bfa227 --- /dev/null +++ b/extensions/libip6t_frag.man @@ -0,0 +1,20 @@ +This module matches the parameters in Fragment header. +.TP +[\fB!\fP] \fB\-\-fragid\fP \fIid\fP[\fB:\fP\fIid\fP] +Matches the given Identification or range of it. +.TP +[\fB!\fP] \fB\-\-fraglen\fP \fIlength\fP +This option cannot be used with kernel version 2.6.10 or later. The length of +Fragment header is static and this option doesn't make sense. +.TP +\fB\-\-fragres\fP +Matches if the reserved fields are filled with zero. +.TP +\fB\-\-fragfirst\fP +Matches on the first fragment. +.TP +\fB\-\-fragmore\fP +Matches if there are more fragments. +.TP +\fB\-\-fraglast\fP +Matches if this is the last fragment. diff --git a/extensions/libip6t_hbh.c b/extensions/libip6t_hbh.c new file mode 100644 index 0000000..520ec9e --- /dev/null +++ b/extensions/libip6t_hbh.c @@ -0,0 +1,228 @@ +/* Shared library add-on to ip6tables to add Hop-by-Hop header support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <errno.h> +#include <xtables.h> +/*#include <linux/in6.h>*/ +#include <linux/netfilter_ipv6/ip6t_opts.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +#define DEBUG 0 + +static void hbh_help(void) +{ + printf( +"hbh match options:\n" +"[!] --hbh-len length total length of this header\n" +" --hbh-opts TYPE[:LEN][,TYPE[:LEN]...] \n" +" Options and its length (list, max: %d)\n", +IP6T_OPTS_OPTSNR); +} + +static const struct option hbh_opts[] = { + { "hbh-len", 1, NULL, '1' }, + { "hbh-opts", 1, NULL, '2' }, + { "hbh-not-strict", 1, NULL, '3' }, + { .name = NULL } +}; + +static u_int32_t +parse_opts_num(const char *idstr, const char *typestr) +{ + unsigned long int id; + char* ep; + + id = strtoul(idstr,&ep,0) ; + + if ( idstr == ep ) { + xtables_error(PARAMETER_PROBLEM, + "hbh: no valid digits in %s `%s'", typestr, idstr); + } + if ( id == ULONG_MAX && errno == ERANGE ) { + xtables_error(PARAMETER_PROBLEM, + "%s `%s' specified too big: would overflow", + typestr, idstr); + } + if ( *idstr != '\0' && *ep != '\0' ) { + xtables_error(PARAMETER_PROBLEM, + "hbh: error parsing %s `%s'", typestr, idstr); + } + return id; +} + +static int +parse_options(const char *optsstr, u_int16_t *opts) +{ + char *buffer, *cp, *next, *range; + unsigned int i; + + buffer = strdup(optsstr); + if (!buffer) xtables_error(OTHER_PROBLEM, "strdup failed"); + + for (cp=buffer, i=0; cp && i<IP6T_OPTS_OPTSNR; cp=next,i++) + { + next=strchr(cp, ','); + if (next) *next++='\0'; + range = strchr(cp, ':'); + if (range) { + if (i == IP6T_OPTS_OPTSNR-1) + xtables_error(PARAMETER_PROBLEM, + "too many ports specified"); + *range++ = '\0'; + } + opts[i] = (parse_opts_num(cp, "opt") & 0xFF) << 8; + if (range) { + if (opts[i] == 0) + xtables_error(PARAMETER_PROBLEM, "PAD0 has not got length"); + opts[i] |= parse_opts_num(range, "length") & 0xFF; + } else { + opts[i] |= (0x00FF); + } + +#if DEBUG + printf("opts str: %s %s\n", cp, range); + printf("opts opt: %04X\n", opts[i]); +#endif + } + if (cp) xtables_error(PARAMETER_PROBLEM, "too many addresses specified"); + + free(buffer); + +#if DEBUG + printf("addr nr: %d\n", i); +#endif + + return i; +} + +static void hbh_init(struct xt_entry_match *m) +{ + struct ip6t_opts *optinfo = (struct ip6t_opts *)m->data; + + optinfo->hdrlen = 0; + optinfo->flags = 0; + optinfo->invflags = 0; + optinfo->optsnr = 0; +} + +static int hbh_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ip6t_opts *optinfo = (struct ip6t_opts *)(*match)->data; + + switch (c) { + case '1': + if (*flags & IP6T_OPTS_LEN) + xtables_error(PARAMETER_PROBLEM, + "Only one `--hbh-len' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + optinfo->hdrlen = parse_opts_num(optarg, "length"); + if (invert) + optinfo->invflags |= IP6T_OPTS_INV_LEN; + optinfo->flags |= IP6T_OPTS_LEN; + *flags |= IP6T_OPTS_LEN; + break; + case '2': + if (*flags & IP6T_OPTS_OPTS) + xtables_error(PARAMETER_PROBLEM, + "Only one `--hbh-opts' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) + xtables_error(PARAMETER_PROBLEM, + " '!' not allowed with `--hbh-opts'"); + optinfo->optsnr = parse_options(optarg, optinfo->opts); + optinfo->flags |= IP6T_OPTS_OPTS; + *flags |= IP6T_OPTS_OPTS; + break; + case '3': + if (*flags & IP6T_OPTS_NSTRICT) + xtables_error(PARAMETER_PROBLEM, + "Only one `--hbh-not-strict' allowed"); + if ( !(*flags & IP6T_OPTS_OPTS) ) + xtables_error(PARAMETER_PROBLEM, + "`--hbh-opts ...' required before `--hbh-not-strict'"); + optinfo->flags |= IP6T_OPTS_NSTRICT; + *flags |= IP6T_OPTS_NSTRICT; + break; + default: + return 0; + } + + return 1; +} + +static void +print_options(unsigned int optsnr, u_int16_t *optsp) +{ + unsigned int i; + + for(i=0; i<optsnr; i++){ + printf("%d", (optsp[i] & 0xFF00)>>8); + if ((optsp[i] & 0x00FF) != 0x00FF){ + printf(":%d", (optsp[i] & 0x00FF)); + } + printf("%c", (i!=optsnr-1)?',':' '); + } +} + +static void hbh_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data; + + printf("hbh "); + if (optinfo->flags & IP6T_OPTS_LEN) { + printf("length"); + printf(":%s", optinfo->invflags & IP6T_OPTS_INV_LEN ? "!" : ""); + printf("%u", optinfo->hdrlen); + printf(" "); + } + if (optinfo->flags & IP6T_OPTS_OPTS) printf("opts "); + print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts); + if (optinfo->flags & IP6T_OPTS_NSTRICT) printf("not-strict "); + if (optinfo->invflags & ~IP6T_OPTS_INV_MASK) + printf("Unknown invflags: 0x%X ", + optinfo->invflags & ~IP6T_OPTS_INV_MASK); +} + +static void hbh_save(const void *ip, const struct xt_entry_match *match) +{ + const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data; + + if (optinfo->flags & IP6T_OPTS_LEN) { + printf("%s--hbh-len %u ", + (optinfo->invflags & IP6T_OPTS_INV_LEN) ? "! " : "", + optinfo->hdrlen); + } + + if (optinfo->flags & IP6T_OPTS_OPTS) + printf("--hbh-opts "); + print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts); + if (optinfo->flags & IP6T_OPTS_NSTRICT) + printf("--hbh-not-strict "); +} + +static struct xtables_match hbh_mt6_reg = { + .name = "hbh", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct ip6t_opts)), + .userspacesize = XT_ALIGN(sizeof(struct ip6t_opts)), + .help = hbh_help, + .init = hbh_init, + .parse = hbh_parse, + .print = hbh_print, + .save = hbh_save, + .extra_opts = hbh_opts, +}; + +void +_init(void) +{ + xtables_register_match(&hbh_mt6_reg); +} diff --git a/extensions/libip6t_hbh.man b/extensions/libip6t_hbh.man new file mode 100644 index 0000000..2d92e04 --- /dev/null +++ b/extensions/libip6t_hbh.man @@ -0,0 +1,7 @@ +This module matches the parameters in Hop-by-Hop Options header +.TP +[\fB!\fP] \fB\-\-hbh\-len\fP \fIlength\fP +Total length of this header in octets. +.TP +\fB\-\-hbh\-opts\fP \fItype\fP[\fB:\fP\fIlength\fP][\fB,\fP\fItype\fP[\fB:\fP\fIlength\fP]...] +numeric type of option and the length of the option data in octets. diff --git a/extensions/libip6t_hl.c b/extensions/libip6t_hl.c new file mode 100644 index 0000000..09589b1 --- /dev/null +++ b/extensions/libip6t_hl.c @@ -0,0 +1,144 @@ +/* + * IPv6 Hop Limit matching module + * Maciej Soltysiak <solt@dns.toxicfilms.tv> + * Based on HW's ttl match + * This program is released under the terms of GNU GPL + * Cleanups by Stephane Ouellette <ouellettes@videotron.ca> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <xtables.h> + +#include <linux/netfilter_ipv6/ip6t_hl.h> + +static void hl_help(void) +{ + printf( +"hl match options:\n" +"[!] --hl-eq value Match hop limit value\n" +" --hl-lt value Match HL < value\n" +" --hl-gt value Match HL > value\n"); +} + +static int hl_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ip6t_hl_info *info = (struct ip6t_hl_info *) (*match)->data; + u_int8_t value; + + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + value = atoi(optarg); + + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "Can't specify HL option twice"); + + if (!optarg) + xtables_error(PARAMETER_PROBLEM, + "hl: You must specify a value"); + switch (c) { + case '2': + if (invert) + info->mode = IP6T_HL_NE; + else + info->mode = IP6T_HL_EQ; + + /* is 0 allowed? */ + info->hop_limit = value; + *flags = 1; + + break; + case '3': + if (invert) + xtables_error(PARAMETER_PROBLEM, + "hl: unexpected `!'"); + + info->mode = IP6T_HL_LT; + info->hop_limit = value; + *flags = 1; + + break; + case '4': + if (invert) + xtables_error(PARAMETER_PROBLEM, + "hl: unexpected `!'"); + + info->mode = IP6T_HL_GT; + info->hop_limit = value; + *flags = 1; + + break; + default: + return 0; + } + + return 1; +} + +static void hl_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "HL match: You must specify one of " + "`--hl-eq', `--hl-lt', `--hl-gt'"); +} + +static void hl_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + static const char *const op[] = { + [IP6T_HL_EQ] = "==", + [IP6T_HL_NE] = "!=", + [IP6T_HL_LT] = "<", + [IP6T_HL_GT] = ">" }; + + const struct ip6t_hl_info *info = + (struct ip6t_hl_info *) match->data; + + printf("HL match HL %s %u ", op[info->mode], info->hop_limit); +} + +static void hl_save(const void *ip, const struct xt_entry_match *match) +{ + static const char *const op[] = { + [IP6T_HL_EQ] = "--hl-eq", + [IP6T_HL_NE] = "! --hl-eq", + [IP6T_HL_LT] = "--hl-lt", + [IP6T_HL_GT] = "--hl-gt" }; + + const struct ip6t_hl_info *info = + (struct ip6t_hl_info *) match->data; + + printf("%s %u ", op[info->mode], info->hop_limit); +} + +static const struct option hl_opts[] = { + { .name = "hl", .has_arg = 1, .val = '2' }, + { .name = "hl-eq", .has_arg = 1, .val = '2' }, + { .name = "hl-lt", .has_arg = 1, .val = '3' }, + { .name = "hl-gt", .has_arg = 1, .val = '4' }, + { .name = NULL } +}; + +static struct xtables_match hl_mt6_reg = { + .name = "hl", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct ip6t_hl_info)), + .userspacesize = XT_ALIGN(sizeof(struct ip6t_hl_info)), + .help = hl_help, + .parse = hl_parse, + .final_check = hl_check, + .print = hl_print, + .save = hl_save, + .extra_opts = hl_opts, +}; + + +void _init(void) +{ + xtables_register_match(&hl_mt6_reg); +} diff --git a/extensions/libip6t_hl.man b/extensions/libip6t_hl.man new file mode 100644 index 0000000..dfbfaf8 --- /dev/null +++ b/extensions/libip6t_hl.man @@ -0,0 +1,10 @@ +This module matches the Hop Limit field in the IPv6 header. +.TP +[\fB!\fP] \fB\-\-hl\-eq\fP \fIvalue\fP +Matches if Hop Limit equals \fIvalue\fP. +.TP +\fB\-\-hl\-lt\fP \fIvalue\fP +Matches if Hop Limit is less than \fIvalue\fP. +.TP +\fB\-\-hl\-gt\fP \fIvalue\fP +Matches if Hop Limit is greater than \fIvalue\fP. diff --git a/extensions/libip6t_icmp6.c b/extensions/libip6t_icmp6.c new file mode 100644 index 0000000..fb321b3 --- /dev/null +++ b/extensions/libip6t_icmp6.c @@ -0,0 +1,263 @@ +/* Shared library add-on to ip6tables to add ICMP support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <limits.h> /* INT_MAX in ip6_tables.h */ +#include <linux/netfilter_ipv6/ip6_tables.h> + +struct icmpv6_names { + const char *name; + u_int8_t type; + u_int8_t code_min, code_max; +}; + +static const struct icmpv6_names icmpv6_codes[] = { + { "destination-unreachable", 1, 0, 0xFF }, + { "no-route", 1, 0, 0 }, + { "communication-prohibited", 1, 1, 1 }, + { "address-unreachable", 1, 3, 3 }, + { "port-unreachable", 1, 4, 4 }, + + { "packet-too-big", 2, 0, 0xFF }, + + { "time-exceeded", 3, 0, 0xFF }, + /* Alias */ { "ttl-exceeded", 3, 0, 0xFF }, + { "ttl-zero-during-transit", 3, 0, 0 }, + { "ttl-zero-during-reassembly", 3, 1, 1 }, + + { "parameter-problem", 4, 0, 0xFF }, + { "bad-header", 4, 0, 0 }, + { "unknown-header-type", 4, 1, 1 }, + { "unknown-option", 4, 2, 2 }, + + { "echo-request", 128, 0, 0xFF }, + /* Alias */ { "ping", 128, 0, 0xFF }, + + { "echo-reply", 129, 0, 0xFF }, + /* Alias */ { "pong", 129, 0, 0xFF }, + + { "router-solicitation", 133, 0, 0xFF }, + + { "router-advertisement", 134, 0, 0xFF }, + + { "neighbour-solicitation", 135, 0, 0xFF }, + /* Alias */ { "neighbor-solicitation", 135, 0, 0xFF }, + + { "neighbour-advertisement", 136, 0, 0xFF }, + /* Alias */ { "neighbor-advertisement", 136, 0, 0xFF }, + + { "redirect", 137, 0, 0xFF }, + +}; + +static void +print_icmpv6types(void) +{ + unsigned int i; + printf("Valid ICMPv6 Types:"); + + for (i = 0; i < ARRAY_SIZE(icmpv6_codes); ++i) { + if (i && icmpv6_codes[i].type == icmpv6_codes[i-1].type) { + if (icmpv6_codes[i].code_min == icmpv6_codes[i-1].code_min + && (icmpv6_codes[i].code_max + == icmpv6_codes[i-1].code_max)) + printf(" (%s)", icmpv6_codes[i].name); + else + printf("\n %s", icmpv6_codes[i].name); + } + else + printf("\n%s", icmpv6_codes[i].name); + } + printf("\n"); +} + +static void icmp6_help(void) +{ + printf( +"icmpv6 match options:\n" +"[!] --icmpv6-type typename match icmpv6 type\n" +" (or numeric type or type/code)\n"); + print_icmpv6types(); +} + +static const struct option icmp6_opts[] = { + { "icmpv6-type", 1, NULL, '1' }, + { .name = NULL } +}; + +static void +parse_icmpv6(const char *icmpv6type, u_int8_t *type, u_int8_t code[]) +{ + static const unsigned int limit = ARRAY_SIZE(icmpv6_codes); + unsigned int match = limit; + unsigned int i; + + for (i = 0; i < limit; i++) { + if (strncasecmp(icmpv6_codes[i].name, icmpv6type, strlen(icmpv6type)) + == 0) { + if (match != limit) + xtables_error(PARAMETER_PROBLEM, + "Ambiguous ICMPv6 type `%s':" + " `%s' or `%s'?", + icmpv6type, + icmpv6_codes[match].name, + icmpv6_codes[i].name); + match = i; + } + } + + if (match != limit) { + *type = icmpv6_codes[match].type; + code[0] = icmpv6_codes[match].code_min; + code[1] = icmpv6_codes[match].code_max; + } else { + char *slash; + char buffer[strlen(icmpv6type) + 1]; + unsigned int number; + + strcpy(buffer, icmpv6type); + slash = strchr(buffer, '/'); + + if (slash) + *slash = '\0'; + + if (!xtables_strtoui(buffer, NULL, &number, 0, UINT8_MAX)) + xtables_error(PARAMETER_PROBLEM, + "Invalid ICMPv6 type `%s'\n", buffer); + *type = number; + if (slash) { + if (!xtables_strtoui(slash+1, NULL, &number, 0, UINT8_MAX)) + xtables_error(PARAMETER_PROBLEM, + "Invalid ICMPv6 code `%s'\n", + slash+1); + code[0] = code[1] = number; + } else { + code[0] = 0; + code[1] = 0xFF; + } + } +} + +static void icmp6_init(struct xt_entry_match *m) +{ + struct ip6t_icmp *icmpv6info = (struct ip6t_icmp *)m->data; + + icmpv6info->code[1] = 0xFF; +} + +static int icmp6_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ip6t_icmp *icmpv6info = (struct ip6t_icmp *)(*match)->data; + + switch (c) { + case '1': + if (*flags == 1) + xtables_error(PARAMETER_PROBLEM, + "icmpv6 match: only use --icmpv6-type once!"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_icmpv6(optarg, &icmpv6info->type, + icmpv6info->code); + if (invert) + icmpv6info->invflags |= IP6T_ICMP_INV; + *flags = 1; + break; + + default: + return 0; + } + + return 1; +} + +static void print_icmpv6type(u_int8_t type, + u_int8_t code_min, u_int8_t code_max, + int invert, + int numeric) +{ + if (!numeric) { + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(icmpv6_codes); ++i) + if (icmpv6_codes[i].type == type + && icmpv6_codes[i].code_min == code_min + && icmpv6_codes[i].code_max == code_max) + break; + + if (i != ARRAY_SIZE(icmpv6_codes)) { + printf("%s%s ", + invert ? "!" : "", + icmpv6_codes[i].name); + return; + } + } + + if (invert) + printf("!"); + + printf("type %u", type); + if (code_min == 0 && code_max == 0xFF) + printf(" "); + else if (code_min == code_max) + printf(" code %u ", code_min); + else + printf(" codes %u-%u ", code_min, code_max); +} + +static void icmp6_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ip6t_icmp *icmpv6 = (struct ip6t_icmp *)match->data; + + printf("ipv6-icmp "); + print_icmpv6type(icmpv6->type, icmpv6->code[0], icmpv6->code[1], + icmpv6->invflags & IP6T_ICMP_INV, + numeric); + + if (icmpv6->invflags & ~IP6T_ICMP_INV) + printf("Unknown invflags: 0x%X ", + icmpv6->invflags & ~IP6T_ICMP_INV); +} + +static void icmp6_save(const void *ip, const struct xt_entry_match *match) +{ + const struct ip6t_icmp *icmpv6 = (struct ip6t_icmp *)match->data; + + if (icmpv6->invflags & IP6T_ICMP_INV) + printf("! "); + + printf("--icmpv6-type %u", icmpv6->type); + if (icmpv6->code[0] != 0 || icmpv6->code[1] != 0xFF) + printf("/%u", icmpv6->code[0]); + printf(" "); +} + +static void icmp6_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "icmpv6 match: You must specify `--icmpv6-type'"); +} + +static struct xtables_match icmp6_mt6_reg = { + .name = "icmp6", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct ip6t_icmp)), + .userspacesize = XT_ALIGN(sizeof(struct ip6t_icmp)), + .help = icmp6_help, + .init = icmp6_init, + .parse = icmp6_parse, + .final_check = icmp6_check, + .print = icmp6_print, + .save = icmp6_save, + .extra_opts = icmp6_opts, +}; + +void _init(void) +{ + xtables_register_match(&icmp6_mt6_reg); +} diff --git a/extensions/libip6t_icmp6.man b/extensions/libip6t_icmp6.man new file mode 100644 index 0000000..817e21c --- /dev/null +++ b/extensions/libip6t_icmp6.man @@ -0,0 +1,14 @@ +This extension can be used if `\-\-protocol ipv6\-icmp' or `\-\-protocol icmpv6' is +specified. It provides the following option: +.TP +[\fB!\fP] \fB\-\-icmpv6\-type\fP \fItype\fP[\fB/\fP\fIcode\fP]|\fItypename\fP +This allows specification of the ICMPv6 type, which can be a numeric +ICMPv6 +.IR type , +.IR type +and +.IR code , +or one of the ICMPv6 type names shown by the command +.nf + ip6tables \-p ipv6\-icmp \-h +.fi diff --git a/extensions/libip6t_ipv6header.c b/extensions/libip6t_ipv6header.c new file mode 100644 index 0000000..af1f5ef --- /dev/null +++ b/extensions/libip6t_ipv6header.c @@ -0,0 +1,290 @@ +/* ipv6header match - matches IPv6 packets based +on whether they contain certain headers */ + +/* Original idea: Brad Chapman + * Rewritten by: Andras Kis-Szabo <kisza@sch.bme.hu> */ + +#include <getopt.h> +#include <xtables.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> +#include <sys/types.h> + +#include <linux/netfilter_ipv6/ip6t_ipv6header.h> + +/* This maybe required +#include <linux/in.h> +#include <linux/in6.h> +*/ + + +/* A few hardcoded protocols for 'all' and in case the user has no + * /etc/protocols */ +struct pprot { + char *name; + u_int8_t num; +}; + +struct numflag { + u_int8_t proto; + u_int8_t flag; +}; + +static const struct pprot chain_protos[] = { + { "hop-by-hop", IPPROTO_HOPOPTS }, + { "protocol", IPPROTO_RAW }, + { "hop", IPPROTO_HOPOPTS }, + { "dst", IPPROTO_DSTOPTS }, + { "route", IPPROTO_ROUTING }, + { "frag", IPPROTO_FRAGMENT }, + { "auth", IPPROTO_AH }, + { "esp", IPPROTO_ESP }, + { "none", IPPROTO_NONE }, + { "prot", IPPROTO_RAW }, + { "0", IPPROTO_HOPOPTS }, + { "60", IPPROTO_DSTOPTS }, + { "43", IPPROTO_ROUTING }, + { "44", IPPROTO_FRAGMENT }, + { "51", IPPROTO_AH }, + { "50", IPPROTO_ESP }, + { "59", IPPROTO_NONE }, + { "255", IPPROTO_RAW }, + /* { "all", 0 }, */ +}; + +static const struct numflag chain_flags[] = { + { IPPROTO_HOPOPTS, MASK_HOPOPTS }, + { IPPROTO_DSTOPTS, MASK_DSTOPTS }, + { IPPROTO_ROUTING, MASK_ROUTING }, + { IPPROTO_FRAGMENT, MASK_FRAGMENT }, + { IPPROTO_AH, MASK_AH }, + { IPPROTO_ESP, MASK_ESP }, + { IPPROTO_NONE, MASK_NONE }, + { IPPROTO_RAW, MASK_PROTO }, +}; + +static char * +proto_to_name(u_int8_t proto, int nolookup) +{ + unsigned int i; + + if (proto && !nolookup) { + struct protoent *pent = getprotobynumber(proto); + if (pent) + return pent->p_name; + } + + for (i = 0; i < ARRAY_SIZE(chain_protos); ++i) + if (chain_protos[i].num == proto) + return chain_protos[i].name; + + return NULL; +} + +static u_int16_t +name_to_proto(const char *s) +{ + unsigned int proto=0; + struct protoent *pent; + + if ((pent = getprotobyname(s))) + proto = pent->p_proto; + else { + unsigned int i; + for (i = 0; i < ARRAY_SIZE(chain_protos); ++i) + if (strcmp(s, chain_protos[i].name) == 0) { + proto = chain_protos[i].num; + break; + } + + if (i == ARRAY_SIZE(chain_protos)) + xtables_error(PARAMETER_PROBLEM, + "unknown header `%s' specified", + s); + } + + return proto; +} + +static unsigned int +add_proto_to_mask(int proto){ + unsigned int i=0, flag=0; + + for (i = 0; i < ARRAY_SIZE(chain_flags); ++i) + if (proto == chain_flags[i].proto){ + flag = chain_flags[i].flag; + break; + } + + if (i == ARRAY_SIZE(chain_flags)) + xtables_error(PARAMETER_PROBLEM, + "unknown header `%d' specified", + proto); + + return flag; +} + +static void ipv6header_help(void) +{ + printf( +"ipv6header match options:\n" +"[!] --header headers Type of header to match, by name\n" +" names: hop,dst,route,frag,auth,esp,none,proto\n" +" long names: hop-by-hop,ipv6-opts,ipv6-route,\n" +" ipv6-frag,ah,esp,ipv6-nonxt,protocol\n" +" numbers: 0,60,43,44,51,50,59\n" +"--soft The header CONTAINS the specified extensions\n"); +} + +static const struct option ipv6header_opts[] = { + { "header", 1, NULL, '1' }, + { "soft", 0, NULL, '2' }, + { .name = NULL } +}; + +static void ipv6header_init(struct xt_entry_match *m) +{ + struct ip6t_ipv6header_info *info = (struct ip6t_ipv6header_info *)m->data; + + info->matchflags = 0x00; + info->invflags = 0x00; + info->modeflag = 0x00; +} + +static unsigned int +parse_header(const char *flags) { + unsigned int ret = 0; + char *ptr; + char *buffer; + + buffer = strdup(flags); + + for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) + ret |= add_proto_to_mask(name_to_proto(ptr)); + + free(buffer); + return ret; +} + +#define IPV6_HDR_HEADER 0x01 +#define IPV6_HDR_SOFT 0x02 + +static int +ipv6header_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ip6t_ipv6header_info *info = (struct ip6t_ipv6header_info *)(*match)->data; + + switch (c) { + case '1' : + /* Parse the provided header names */ + if (*flags & IPV6_HDR_HEADER) + xtables_error(PARAMETER_PROBLEM, + "Only one `--header' allowed"); + + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + if (! (info->matchflags = parse_header(optarg)) ) + xtables_error(PARAMETER_PROBLEM, "ip6t_ipv6header: cannot parse header names"); + + if (invert) + info->invflags |= 0xFF; + *flags |= IPV6_HDR_HEADER; + break; + case '2' : + /* Soft-mode requested? */ + if (*flags & IPV6_HDR_SOFT) + xtables_error(PARAMETER_PROBLEM, + "Only one `--soft' allowed"); + + info->modeflag |= 0xFF; + *flags |= IPV6_HDR_SOFT; + break; + default: + return 0; + } + + return 1; +} + +static void ipv6header_check(unsigned int flags) +{ + if (!flags) xtables_error(PARAMETER_PROBLEM, "ip6t_ipv6header: no options specified"); +} + +static void +print_header(u_int8_t flags){ + int have_flag = 0; + + while (flags) { + unsigned int i; + + for (i = 0; (flags & chain_flags[i].flag) == 0; i++); + + if (have_flag) + printf(","); + + printf("%s", proto_to_name(chain_flags[i].proto,0)); + have_flag = 1; + + flags &= ~chain_flags[i].flag; + } + + if (!have_flag) + printf("NONE"); +} + +static void ipv6header_print(const void *ip, + const struct xt_entry_match *match, int numeric) +{ + const struct ip6t_ipv6header_info *info = (const struct ip6t_ipv6header_info *)match->data; + printf("ipv6header "); + + if (info->matchflags || info->invflags) { + printf("flags:%s", info->invflags ? "!" : ""); + if (numeric) + printf("0x%02X ", info->matchflags); + else { + print_header(info->matchflags); + printf(" "); + } + } + + if (info->modeflag) + printf("soft "); +} + +static void ipv6header_save(const void *ip, const struct xt_entry_match *match) +{ + + const struct ip6t_ipv6header_info *info = (const struct ip6t_ipv6header_info *)match->data; + + printf("%s--header ", info->invflags ? "! " : ""); + print_header(info->matchflags); + printf(" "); + if (info->modeflag) + printf("--soft "); +} + +static struct xtables_match ipv6header_mt6_reg = { + .name = "ipv6header", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct ip6t_ipv6header_info)), + .userspacesize = XT_ALIGN(sizeof(struct ip6t_ipv6header_info)), + .help = ipv6header_help, + .init = ipv6header_init, + .parse = ipv6header_parse, + .final_check = ipv6header_check, + .print = ipv6header_print, + .save = ipv6header_save, + .extra_opts = ipv6header_opts, +}; + +void _init(void) +{ + xtables_register_match(&ipv6header_mt6_reg); +} diff --git a/extensions/libip6t_ipv6header.man b/extensions/libip6t_ipv6header.man new file mode 100644 index 0000000..a998861 --- /dev/null +++ b/extensions/libip6t_ipv6header.man @@ -0,0 +1,37 @@ +This module matches IPv6 extension headers and/or upper layer header. +.TP +\fB\-\-soft\fP +Matches if the packet includes \fBany\fP of the headers specified with +\fB\-\-header\fP. +.TP +[\fB!\fP] \fB\-\-header\fP \fIheader\fP[\fB,\fP\fIheader\fP...] +Matches the packet which EXACTLY includes all specified headers. The headers +encapsulated with ESP header are out of scope. +Possible \fIheader\fP types can be: +.TP +\fBhop\fP|\fBhop\-by\-hop\fP +Hop-by-Hop Options header +.TP +\fBdst\fP +Destination Options header +.TP +\fBroute\fP +Routing header +.TP +\fBfrag\fP +Fragment header +.TP +\fBauth\fP +Authentication header +.TP +\fBesp\fP +Encapsulating Security Payload header +.TP +\fBnone\fP +No Next header which matches 59 in the 'Next Header field' of IPv6 header or +any IPv6 extension headers +.TP +\fBproto\fP +which matches any upper layer protocol header. A protocol name from +/etc/protocols and numeric value also allowed. The number 255 is equivalent to +\fBproto\fP. diff --git a/extensions/libip6t_mh.c b/extensions/libip6t_mh.c new file mode 100644 index 0000000..95cd65d --- /dev/null +++ b/extensions/libip6t_mh.c @@ -0,0 +1,240 @@ +/* Shared library add-on to ip6tables to add mobility header support. */ +/* + * Copyright (C)2006 USAGI/WIDE Project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Author: + * Masahide NAKAMURA @USAGI <masahide.nakamura.cz@hitachi.com> + * + * Based on libip6t_{icmpv6,udp}.c + */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <linux/netfilter_ipv6/ip6t_mh.h> + +struct mh_name { + const char *name; + u_int8_t type; +}; + +static const struct mh_name mh_names[] = { + { "binding-refresh-request", 0, }, + /* Alias */ { "brr", 0, }, + { "home-test-init", 1, }, + /* Alias */ { "hoti", 1, }, + { "careof-test-init", 2, }, + /* Alias */ { "coti", 2, }, + { "home-test", 3, }, + /* Alias */ { "hot", 3, }, + { "careof-test", 4, }, + /* Alias */ { "cot", 4, }, + { "binding-update", 5, }, + /* Alias */ { "bu", 5, }, + { "binding-acknowledgement", 6, }, + /* Alias */ { "ba", 6, }, + { "binding-error", 7, }, + /* Alias */ { "be", 7, }, +}; + +static void print_types_all(void) +{ + unsigned int i; + printf("Valid MH types:"); + + for (i = 0; i < ARRAY_SIZE(mh_names); ++i) { + if (i && mh_names[i].type == mh_names[i-1].type) + printf(" (%s)", mh_names[i].name); + else + printf("\n%s", mh_names[i].name); + } + printf("\n"); +} + +static void mh_help(void) +{ + printf( +"mh match options:\n" +"[!] --mh-type type[:type] match mh type\n"); + print_types_all(); +} + +static void mh_init(struct xt_entry_match *m) +{ + struct ip6t_mh *mhinfo = (struct ip6t_mh *)m->data; + + mhinfo->types[1] = 0xFF; +} + +static unsigned int name_to_type(const char *name) +{ + int namelen = strlen(name); + static const unsigned int limit = ARRAY_SIZE(mh_names); + unsigned int match = limit; + unsigned int i; + + for (i = 0; i < limit; i++) { + if (strncasecmp(mh_names[i].name, name, namelen) == 0) { + int len = strlen(mh_names[i].name); + if (match == limit || len == namelen) + match = i; + } + } + + if (match != limit) { + return mh_names[match].type; + } else { + unsigned int number; + + if (!xtables_strtoui(name, NULL, &number, 0, UINT8_MAX)) + xtables_error(PARAMETER_PROBLEM, + "Invalid MH type `%s'\n", name); + return number; + } +} + +static void parse_mh_types(const char *mhtype, u_int8_t *types) +{ + char *buffer; + char *cp; + + buffer = strdup(mhtype); + if ((cp = strchr(buffer, ':')) == NULL) + types[0] = types[1] = name_to_type(buffer); + else { + *cp = '\0'; + cp++; + + types[0] = buffer[0] ? name_to_type(buffer) : 0; + types[1] = cp[0] ? name_to_type(cp) : 0xFF; + + if (types[0] > types[1]) + xtables_error(PARAMETER_PROBLEM, + "Invalid MH type range (min > max)"); + } + free(buffer); +} + +#define MH_TYPES 0x01 + +static int mh_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ip6t_mh *mhinfo = (struct ip6t_mh *)(*match)->data; + + switch (c) { + case '1': + if (*flags & MH_TYPES) + xtables_error(PARAMETER_PROBLEM, + "Only one `--mh-type' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_mh_types(optarg, mhinfo->types); + if (invert) + mhinfo->invflags |= IP6T_MH_INV_TYPE; + *flags |= MH_TYPES; + break; + + default: + return 0; + } + + return 1; +} + +static const char *type_to_name(u_int8_t type) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mh_names); ++i) + if (mh_names[i].type == type) + return mh_names[i].name; + + return NULL; +} + +static void print_type(u_int8_t type, int numeric) +{ + const char *name; + if (numeric || !(name = type_to_name(type))) + printf("%u", type); + else + printf("%s", name); +} + +static void print_types(u_int8_t min, u_int8_t max, int invert, int numeric) +{ + const char *inv = invert ? "!" : ""; + + if (min != 0 || max != 0xFF || invert) { + if (min == max) { + printf("%s", inv); + print_type(min, numeric); + } else { + printf("%s", inv); + print_type(min, numeric); + printf(":"); + print_type(max, numeric); + } + printf(" "); + } +} + +static void mh_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ip6t_mh *mhinfo = (struct ip6t_mh *)match->data; + + printf("mh "); + print_types(mhinfo->types[0], mhinfo->types[1], + mhinfo->invflags & IP6T_MH_INV_TYPE, + numeric); + if (mhinfo->invflags & ~IP6T_MH_INV_MASK) + printf("Unknown invflags: 0x%X ", + mhinfo->invflags & ~IP6T_MH_INV_MASK); +} + +static void mh_save(const void *ip, const struct xt_entry_match *match) +{ + const struct ip6t_mh *mhinfo = (struct ip6t_mh *)match->data; + + if (mhinfo->types[0] == 0 && mhinfo->types[1] == 0xFF) + return; + + if (mhinfo->invflags & IP6T_MH_INV_TYPE) + printf("! "); + + if (mhinfo->types[0] != mhinfo->types[1]) + printf("--mh-type %u:%u ", mhinfo->types[0], mhinfo->types[1]); + else + printf("--mh-type %u ", mhinfo->types[0]); +} + +static const struct option mh_opts[] = { + { "mh-type", 1, NULL, '1' }, + { .name = NULL } +}; + +static struct xtables_match mh_mt6_reg = { + .name = "mh", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct ip6t_mh)), + .userspacesize = XT_ALIGN(sizeof(struct ip6t_mh)), + .help = mh_help, + .init = mh_init, + .parse = mh_parse, + .print = mh_print, + .save = mh_save, + .extra_opts = mh_opts, +}; + +void _init(void) +{ + xtables_register_match(&mh_mt6_reg); +} diff --git a/extensions/libip6t_mh.man b/extensions/libip6t_mh.man new file mode 100644 index 0000000..4559e78 --- /dev/null +++ b/extensions/libip6t_mh.man @@ -0,0 +1,12 @@ +This extension is loaded if `\-\-protocol ipv6\-mh' or `\-\-protocol mh' is +specified. It provides the following option: +.TP +[\fB!\fP] \fB\-\-mh\-type\fP \fItype\fP[\fB:\fP\fItype\fP] +This allows specification of the Mobility Header(MH) type, which can be +a numeric MH +.IR type , +.IR type +or one of the MH type names shown by the command +.nf + ip6tables \-p ipv6\-mh \-h +.fi diff --git a/extensions/libip6t_rt.c b/extensions/libip6t_rt.c new file mode 100644 index 0000000..a04023d --- /dev/null +++ b/extensions/libip6t_rt.c @@ -0,0 +1,344 @@ +/* Shared library add-on to ip6tables to add Routing header support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <errno.h> +#include <xtables.h> +/*#include <linux/in6.h>*/ +#include <linux/netfilter_ipv6/ip6t_rt.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +/*#define DEBUG 1*/ + +static void rt_help(void) +{ + printf( +"rt match options:\n" +"[!] --rt-type type match the type\n" +"[!] --rt-segsleft num[:num] match the Segments Left field (range)\n" +"[!] --rt-len length total length of this header\n" +" --rt-0-res check the reserved filed, too (type 0)\n" +" --rt-0-addrs ADDR[,ADDR...] Type=0 addresses (list, max: %d)\n" +" --rt-0-not-strict List of Type=0 addresses not a strict list\n", +IP6T_RT_HOPS); +} + +static const struct option rt_opts[] = { + { "rt-type", 1, NULL, '1' }, + { "rt-segsleft", 1, NULL, '2' }, + { "rt-len", 1, NULL, '3' }, + { "rt-0-res", 0, NULL, '4' }, + { "rt-0-addrs", 1, NULL, '5' }, + { "rt-0-not-strict", 0, NULL, '6' }, + { .name = NULL } +}; + +static u_int32_t +parse_rt_num(const char *idstr, const char *typestr) +{ + unsigned long int id; + char* ep; + + id = strtoul(idstr,&ep,0) ; + + if ( idstr == ep ) { + xtables_error(PARAMETER_PROBLEM, + "RT no valid digits in %s `%s'", typestr, idstr); + } + if ( id == ULONG_MAX && errno == ERANGE ) { + xtables_error(PARAMETER_PROBLEM, + "%s `%s' specified too big: would overflow", + typestr, idstr); + } + if ( *idstr != '\0' && *ep != '\0' ) { + xtables_error(PARAMETER_PROBLEM, + "RT error parsing %s `%s'", typestr, idstr); + } + return id; +} + +static void +parse_rt_segsleft(const char *idstring, u_int32_t *ids) +{ + char *buffer; + char *cp; + + buffer = strdup(idstring); + if ((cp = strchr(buffer, ':')) == NULL) + ids[0] = ids[1] = parse_rt_num(buffer,"segsleft"); + else { + *cp = '\0'; + cp++; + + ids[0] = buffer[0] ? parse_rt_num(buffer,"segsleft") : 0; + ids[1] = cp[0] ? parse_rt_num(cp,"segsleft") : 0xFFFFFFFF; + } + free(buffer); +} + +static char * +addr_to_numeric(const struct in6_addr *addrp) +{ + static char buf[50+1]; + return (char *)inet_ntop(AF_INET6, addrp, buf, sizeof(buf)); +} + +static struct in6_addr * +numeric_to_addr(const char *num) +{ + static struct in6_addr ap; + int err; + + if ((err=inet_pton(AF_INET6, num, &ap)) == 1) + return ≈ +#ifdef DEBUG + fprintf(stderr, "\nnumeric2addr: %d\n", err); +#endif + xtables_error(PARAMETER_PROBLEM, "bad address: %s", num); + + return (struct in6_addr *)NULL; +} + + +static int +parse_addresses(const char *addrstr, struct in6_addr *addrp) +{ + char *buffer, *cp, *next; + unsigned int i; + + buffer = strdup(addrstr); + if (!buffer) xtables_error(OTHER_PROBLEM, "strdup failed"); + + for (cp=buffer, i=0; cp && i<IP6T_RT_HOPS; cp=next,i++) + { + next=strchr(cp, ','); + if (next) *next++='\0'; + memcpy(&(addrp[i]), numeric_to_addr(cp), sizeof(struct in6_addr)); +#if DEBUG + printf("addr str: %s\n", cp); + printf("addr ip6: %s\n", addr_to_numeric((numeric_to_addr(cp)))); + printf("addr [%d]: %s\n", i, addr_to_numeric(&(addrp[i]))); +#endif + } + if (cp) xtables_error(PARAMETER_PROBLEM, "too many addresses specified"); + + free(buffer); + +#if DEBUG + printf("addr nr: %d\n", i); +#endif + + return i; +} + +static void rt_init(struct xt_entry_match *m) +{ + struct ip6t_rt *rtinfo = (struct ip6t_rt *)m->data; + + rtinfo->rt_type = 0x0L; + rtinfo->segsleft[0] = 0x0L; + rtinfo->segsleft[1] = 0xFFFFFFFF; + rtinfo->hdrlen = 0; + rtinfo->flags = 0; + rtinfo->invflags = 0; + rtinfo->addrnr = 0; +} + +static int rt_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ip6t_rt *rtinfo = (struct ip6t_rt *)(*match)->data; + + switch (c) { + case '1': + if (*flags & IP6T_RT_TYP) + xtables_error(PARAMETER_PROBLEM, + "Only one `--rt-type' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + rtinfo->rt_type = parse_rt_num(optarg, "type"); + if (invert) + rtinfo->invflags |= IP6T_RT_INV_TYP; + rtinfo->flags |= IP6T_RT_TYP; + *flags |= IP6T_RT_TYP; + break; + case '2': + if (*flags & IP6T_RT_SGS) + xtables_error(PARAMETER_PROBLEM, + "Only one `--rt-segsleft' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_rt_segsleft(optarg, rtinfo->segsleft); + if (invert) + rtinfo->invflags |= IP6T_RT_INV_SGS; + rtinfo->flags |= IP6T_RT_SGS; + *flags |= IP6T_RT_SGS; + break; + case '3': + if (*flags & IP6T_RT_LEN) + xtables_error(PARAMETER_PROBLEM, + "Only one `--rt-len' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + rtinfo->hdrlen = parse_rt_num(optarg, "length"); + if (invert) + rtinfo->invflags |= IP6T_RT_INV_LEN; + rtinfo->flags |= IP6T_RT_LEN; + *flags |= IP6T_RT_LEN; + break; + case '4': + if (*flags & IP6T_RT_RES) + xtables_error(PARAMETER_PROBLEM, + "Only one `--rt-0-res' allowed"); + if ( !(*flags & IP6T_RT_TYP) || (rtinfo->rt_type != 0) || (rtinfo->invflags & IP6T_RT_INV_TYP) ) + xtables_error(PARAMETER_PROBLEM, + "`--rt-type 0' required before `--rt-0-res'"); + rtinfo->flags |= IP6T_RT_RES; + *flags |= IP6T_RT_RES; + break; + case '5': + if (*flags & IP6T_RT_FST) + xtables_error(PARAMETER_PROBLEM, + "Only one `--rt-0-addrs' allowed"); + if ( !(*flags & IP6T_RT_TYP) || (rtinfo->rt_type != 0) || (rtinfo->invflags & IP6T_RT_INV_TYP) ) + xtables_error(PARAMETER_PROBLEM, + "`--rt-type 0' required before `--rt-0-addrs'"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) + xtables_error(PARAMETER_PROBLEM, + " '!' not allowed with `--rt-0-addrs'"); + rtinfo->addrnr = parse_addresses(optarg, rtinfo->addrs); + rtinfo->flags |= IP6T_RT_FST; + *flags |= IP6T_RT_FST; + break; + case '6': + if (*flags & IP6T_RT_FST_NSTRICT) + xtables_error(PARAMETER_PROBLEM, + "Only one `--rt-0-not-strict' allowed"); + if ( !(*flags & IP6T_RT_FST) ) + xtables_error(PARAMETER_PROBLEM, + "`--rt-0-addr ...' required before `--rt-0-not-strict'"); + rtinfo->flags |= IP6T_RT_FST_NSTRICT; + *flags |= IP6T_RT_FST_NSTRICT; + break; + default: + return 0; + } + + return 1; +} + +static void +print_nums(const char *name, u_int32_t min, u_int32_t max, + int invert) +{ + const char *inv = invert ? "!" : ""; + + if (min != 0 || max != 0xFFFFFFFF || invert) { + printf("%s", name); + if (min == max) { + printf(":%s", inv); + printf("%u", min); + } else { + printf("s:%s", inv); + printf("%u",min); + printf(":"); + printf("%u",max); + } + printf(" "); + } +} + +static void +print_addresses(unsigned int addrnr, struct in6_addr *addrp) +{ + unsigned int i; + + for(i=0; i<addrnr; i++){ + printf("%s%c", addr_to_numeric(&(addrp[i])), (i!=addrnr-1)?',':' '); + } +} + +static void rt_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ip6t_rt *rtinfo = (struct ip6t_rt *)match->data; + + printf("rt "); + if (rtinfo->flags & IP6T_RT_TYP) + printf("type:%s%d ", rtinfo->invflags & IP6T_RT_INV_TYP ? "!" : "", + rtinfo->rt_type); + print_nums("segsleft", rtinfo->segsleft[0], rtinfo->segsleft[1], + rtinfo->invflags & IP6T_RT_INV_SGS); + if (rtinfo->flags & IP6T_RT_LEN) { + printf("length"); + printf(":%s", rtinfo->invflags & IP6T_RT_INV_LEN ? "!" : ""); + printf("%u", rtinfo->hdrlen); + printf(" "); + } + if (rtinfo->flags & IP6T_RT_RES) printf("reserved "); + if (rtinfo->flags & IP6T_RT_FST) printf("0-addrs "); + print_addresses(rtinfo->addrnr, (struct in6_addr *)rtinfo->addrs); + if (rtinfo->flags & IP6T_RT_FST_NSTRICT) printf("0-not-strict "); + if (rtinfo->invflags & ~IP6T_RT_INV_MASK) + printf("Unknown invflags: 0x%X ", + rtinfo->invflags & ~IP6T_RT_INV_MASK); +} + +static void rt_save(const void *ip, const struct xt_entry_match *match) +{ + const struct ip6t_rt *rtinfo = (struct ip6t_rt *)match->data; + + if (rtinfo->flags & IP6T_RT_TYP) { + printf("%s--rt-type %u ", + (rtinfo->invflags & IP6T_RT_INV_TYP) ? "! " : "", + rtinfo->rt_type); + } + + if (!(rtinfo->segsleft[0] == 0 + && rtinfo->segsleft[1] == 0xFFFFFFFF)) { + printf("%s--rt-segsleft ", + (rtinfo->invflags & IP6T_RT_INV_SGS) ? "! " : ""); + if (rtinfo->segsleft[0] + != rtinfo->segsleft[1]) + printf("%u:%u ", + rtinfo->segsleft[0], + rtinfo->segsleft[1]); + else + printf("%u ", + rtinfo->segsleft[0]); + } + + if (rtinfo->flags & IP6T_RT_LEN) { + printf("%s--rt-len %u ", + (rtinfo->invflags & IP6T_RT_INV_LEN) ? "! " : "", + rtinfo->hdrlen); + } + + if (rtinfo->flags & IP6T_RT_RES) printf("--rt-0-res "); + if (rtinfo->flags & IP6T_RT_FST) printf("--rt-0-addrs "); + print_addresses(rtinfo->addrnr, (struct in6_addr *)rtinfo->addrs); + if (rtinfo->flags & IP6T_RT_FST_NSTRICT) printf("--rt-0-not-strict "); + +} + +static struct xtables_match rt_mt6_reg = { + .name = "rt", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct ip6t_rt)), + .userspacesize = XT_ALIGN(sizeof(struct ip6t_rt)), + .help = rt_help, + .init = rt_init, + .parse = rt_parse, + .print = rt_print, + .save = rt_save, + .extra_opts = rt_opts, +}; + +void +_init(void) +{ + xtables_register_match(&rt_mt6_reg); +} diff --git a/extensions/libip6t_rt.man b/extensions/libip6t_rt.man new file mode 100644 index 0000000..0443e0a --- /dev/null +++ b/extensions/libip6t_rt.man @@ -0,0 +1,19 @@ +Match on IPv6 routing header +.TP +[\fB!\fP] \fB\-\-rt\-type\fP \fItype\fP +Match the type (numeric). +.TP +[\fB!\fP] \fB\-\-rt\-segsleft\fP \fInum\fP[\fB:\fP\fInum\fP] +Match the `segments left' field (range). +.TP +[\fB!\fP] \fB\-\-rt\-len\fP \fIlength\fP +Match the length of this header. +.TP +\fB\-\-rt\-0\-res\fP +Match the reserved field, too (type=0) +.TP +\fB\-\-rt\-0\-addrs\fP \fIaddr\fP[\fB,\fP\fIaddr\fP...] +Match type=0 addresses (list). +.TP +\fB\-\-rt\-0\-not\-strict\fP +List of type=0 addresses is not a strict list. diff --git a/extensions/libipt_CLUSTERIP.c b/extensions/libipt_CLUSTERIP.c new file mode 100644 index 0000000..279aacf --- /dev/null +++ b/extensions/libipt_CLUSTERIP.c @@ -0,0 +1,249 @@ +/* Shared library add-on to iptables to add CLUSTERIP target support. + * (C) 2003 by Harald Welte <laforge@gnumonks.org> + * + * Development of this code was funded by SuSE AG, http://www.suse.com/ + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <stddef.h> + +#if defined(__GLIBC__) && __GLIBC__ == 2 +#include <net/ethernet.h> +#else +#include <linux/if_ether.h> +#endif + +#include <xtables.h> +#include <linux/netfilter_ipv4/ipt_CLUSTERIP.h> + +static void CLUSTERIP_help(void) +{ + printf( +"CLUSTERIP target options:\n" +" --new Create a new ClusterIP\n" +" --hashmode <mode> Specify hashing mode\n" +" sourceip\n" +" sourceip-sourceport\n" +" sourceip-sourceport-destport\n" +" --clustermac <mac> Set clusterIP MAC address\n" +" --total-nodes <num> Set number of total nodes in cluster\n" +" --local-node <num> Set the local node number\n" +" --hash-init <num> Set init value of the Jenkins hash\n"); +} + +#define PARAM_NEW 0x0001 +#define PARAM_HMODE 0x0002 +#define PARAM_MAC 0x0004 +#define PARAM_TOTALNODE 0x0008 +#define PARAM_LOCALNODE 0x0010 +#define PARAM_HASHINIT 0x0020 + +static const struct option CLUSTERIP_opts[] = { + { "new", 0, NULL, '1' }, + { "hashmode", 1, NULL, '2' }, + { "clustermac", 1, NULL, '3' }, + { "total-nodes", 1, NULL, '4' }, + { "local-node", 1, NULL, '5' }, + { "hash-init", 1, NULL, '6' }, + { .name = NULL } +}; + +static void +parse_mac(const char *mac, char *macbuf) +{ + unsigned int i = 0; + + if (strlen(mac) != ETH_ALEN*3-1) + xtables_error(PARAMETER_PROBLEM, "Bad mac address \"%s\"", mac); + + for (i = 0; i < ETH_ALEN; i++) { + long number; + char *end; + + number = strtol(mac + i*3, &end, 16); + + if (end == mac + i*3 + 2 + && number >= 0 + && number <= 255) + macbuf[i] = number; + else + xtables_error(PARAMETER_PROBLEM, + "Bad mac address `%s'", mac); + } +} + +static int CLUSTERIP_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct ipt_clusterip_tgt_info *cipinfo + = (struct ipt_clusterip_tgt_info *)(*target)->data; + + switch (c) { + unsigned int num; + case '1': + cipinfo->flags |= CLUSTERIP_FLAG_NEW; + if (*flags & PARAM_NEW) + xtables_error(PARAMETER_PROBLEM, "Can only specify \"--new\" once\n"); + *flags |= PARAM_NEW; + break; + case '2': + if (!(*flags & PARAM_NEW)) + xtables_error(PARAMETER_PROBLEM, "Can only specify hashmode combined with \"--new\"\n"); + if (*flags & PARAM_HMODE) + xtables_error(PARAMETER_PROBLEM, "Can only specify hashmode once\n"); + if (!strcmp(optarg, "sourceip")) + cipinfo->hash_mode = CLUSTERIP_HASHMODE_SIP; + else if (!strcmp(optarg, "sourceip-sourceport")) + cipinfo->hash_mode = CLUSTERIP_HASHMODE_SIP_SPT; + else if (!strcmp(optarg, "sourceip-sourceport-destport")) + cipinfo->hash_mode = CLUSTERIP_HASHMODE_SIP_SPT_DPT; + else + xtables_error(PARAMETER_PROBLEM, "Unknown hashmode \"%s\"\n", + optarg); + *flags |= PARAM_HMODE; + break; + case '3': + if (!(*flags & PARAM_NEW)) + xtables_error(PARAMETER_PROBLEM, "Can only specify MAC combined with \"--new\"\n"); + if (*flags & PARAM_MAC) + xtables_error(PARAMETER_PROBLEM, "Can only specify MAC once\n"); + parse_mac(optarg, (char *)cipinfo->clustermac); + if (!(cipinfo->clustermac[0] & 0x01)) + xtables_error(PARAMETER_PROBLEM, "MAC has to be a multicast ethernet address\n"); + *flags |= PARAM_MAC; + break; + case '4': + if (!(*flags & PARAM_NEW)) + xtables_error(PARAMETER_PROBLEM, "Can only specify node number combined with \"--new\"\n"); + if (*flags & PARAM_TOTALNODE) + xtables_error(PARAMETER_PROBLEM, "Can only specify total node number once\n"); + if (!xtables_strtoui(optarg, NULL, &num, 1, CLUSTERIP_MAX_NODES)) + xtables_error(PARAMETER_PROBLEM, "Unable to parse \"%s\"\n", optarg); + cipinfo->num_total_nodes = num; + *flags |= PARAM_TOTALNODE; + break; + case '5': + if (!(*flags & PARAM_NEW)) + xtables_error(PARAMETER_PROBLEM, "Can only specify node number combined with \"--new\"\n"); + if (*flags & PARAM_LOCALNODE) + xtables_error(PARAMETER_PROBLEM, "Can only specify local node number once\n"); + if (!xtables_strtoui(optarg, NULL, &num, 1, CLUSTERIP_MAX_NODES)) + xtables_error(PARAMETER_PROBLEM, "Unable to parse \"%s\"\n", optarg); + cipinfo->num_local_nodes = 1; + cipinfo->local_nodes[0] = num; + *flags |= PARAM_LOCALNODE; + break; + case '6': + if (!(*flags & PARAM_NEW)) + xtables_error(PARAMETER_PROBLEM, "Can only specify hash init value combined with \"--new\"\n"); + if (*flags & PARAM_HASHINIT) + xtables_error(PARAMETER_PROBLEM, "Can specify hash init value only once\n"); + if (!xtables_strtoui(optarg, NULL, &num, 0, UINT_MAX)) + xtables_error(PARAMETER_PROBLEM, "Unable to parse \"%s\"\n", optarg); + cipinfo->hash_initval = num; + *flags |= PARAM_HASHINIT; + break; + default: + return 0; + } + + return 1; +} + +static void CLUSTERIP_check(unsigned int flags) +{ + if (flags == 0) + return; + + if ((flags & (PARAM_NEW|PARAM_HMODE|PARAM_MAC|PARAM_TOTALNODE|PARAM_LOCALNODE)) + == (PARAM_NEW|PARAM_HMODE|PARAM_MAC|PARAM_TOTALNODE|PARAM_LOCALNODE)) + return; + + xtables_error(PARAMETER_PROBLEM, "CLUSTERIP target: Invalid parameter combination\n"); +} + +static char *hashmode2str(enum clusterip_hashmode mode) +{ + char *retstr; + switch (mode) { + case CLUSTERIP_HASHMODE_SIP: + retstr = "sourceip"; + break; + case CLUSTERIP_HASHMODE_SIP_SPT: + retstr = "sourceip-sourceport"; + break; + case CLUSTERIP_HASHMODE_SIP_SPT_DPT: + retstr = "sourceip-sourceport-destport"; + break; + default: + retstr = "unknown-error"; + break; + } + return retstr; +} + +static char *mac2str(const u_int8_t mac[ETH_ALEN]) +{ + static char buf[ETH_ALEN*3]; + sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return buf; +} + +static void CLUSTERIP_print(const void *ip, + const struct xt_entry_target *target, int numeric) +{ + const struct ipt_clusterip_tgt_info *cipinfo = + (const struct ipt_clusterip_tgt_info *)target->data; + + if (!cipinfo->flags & CLUSTERIP_FLAG_NEW) { + printf("CLUSTERIP"); + return; + } + + printf("CLUSTERIP hashmode=%s clustermac=%s total_nodes=%u local_node=%u hash_init=%u", + hashmode2str(cipinfo->hash_mode), + mac2str(cipinfo->clustermac), + cipinfo->num_total_nodes, + cipinfo->local_nodes[0], + cipinfo->hash_initval); +} + +static void CLUSTERIP_save(const void *ip, const struct xt_entry_target *target) +{ + const struct ipt_clusterip_tgt_info *cipinfo = + (const struct ipt_clusterip_tgt_info *)target->data; + + /* if this is not a new entry, we don't need to save target + * parameters */ + if (!cipinfo->flags & CLUSTERIP_FLAG_NEW) + return; + + printf("--new --hashmode %s --clustermac %s --total-nodes %d --local-node %d --hash-init %u", + hashmode2str(cipinfo->hash_mode), + mac2str(cipinfo->clustermac), + cipinfo->num_total_nodes, + cipinfo->local_nodes[0], + cipinfo->hash_initval); +} + +static struct xtables_target clusterip_tg_reg = { + .name = "CLUSTERIP", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct ipt_clusterip_tgt_info)), + .userspacesize = offsetof(struct ipt_clusterip_tgt_info, config), + .help = CLUSTERIP_help, + .parse = CLUSTERIP_parse, + .final_check = CLUSTERIP_check, + .print = CLUSTERIP_print, + .save = CLUSTERIP_save, + .extra_opts = CLUSTERIP_opts, +}; + +void _init(void) +{ + xtables_register_target(&clusterip_tg_reg); +} diff --git a/extensions/libipt_CLUSTERIP.man b/extensions/libipt_CLUSTERIP.man new file mode 100644 index 0000000..8ec6d6b --- /dev/null +++ b/extensions/libipt_CLUSTERIP.man @@ -0,0 +1,24 @@ +This module allows you to configure a simple cluster of nodes that share +a certain IP and MAC address without an explicit load balancer in front of +them. Connections are statically distributed between the nodes in this +cluster. +.TP +\fB\-\-new\fP +Create a new ClusterIP. You always have to set this on the first rule +for a given ClusterIP. +.TP +\fB\-\-hashmode\fP \fImode\fP +Specify the hashing mode. Has to be one of +\fBsourceip\fP, \fBsourceip\-sourceport\fP, \fBsourceip\-sourceport\-destport\fP. +.TP +\fB\-\-clustermac\fP \fImac\fP +Specify the ClusterIP MAC address. Has to be a link\-layer multicast address +.TP +\fB\-\-total\-nodes\fP \fInum\fP +Number of total nodes within this cluster. +.TP +\fB\-\-local\-node\fP \fInum\fP +Local node number within this cluster. +.TP +\fB\-\-hash\-init\fP \fIrnd\fP +Specify the random seed used for hash initialization. diff --git a/extensions/libipt_DNAT.c b/extensions/libipt_DNAT.c new file mode 100644 index 0000000..57c5888 --- /dev/null +++ b/extensions/libipt_DNAT.c @@ -0,0 +1,269 @@ +/* Shared library add-on to iptables to add destination-NAT support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <iptables.h> /* get_kernel_version */ +#include <limits.h> /* INT_MAX in ip_tables.h */ +#include <linux/netfilter_ipv4/ip_tables.h> +#include <net/netfilter/nf_nat.h> + +#define IPT_DNAT_OPT_DEST 0x1 +#define IPT_DNAT_OPT_RANDOM 0x2 + +/* Dest NAT data consists of a multi-range, indicating where to map + to. */ +struct ipt_natinfo +{ + struct xt_entry_target t; + struct nf_nat_multi_range mr; +}; + +static void DNAT_help(void) +{ + printf( +"DNAT target options:\n" +" --to-destination <ipaddr>[-<ipaddr>][:port-port]\n" +" Address to map destination to.\n" +"[--random] [--persistent]\n"); +} + +static const struct option DNAT_opts[] = { + { "to-destination", 1, NULL, '1' }, + { "random", 0, NULL, '2' }, + { "persistent", 0, NULL, '3' }, + { .name = NULL } +}; + +static struct ipt_natinfo * +append_range(struct ipt_natinfo *info, const struct nf_nat_range *range) +{ + unsigned int size; + + /* One rangesize already in struct ipt_natinfo */ + size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range)); + + info = realloc(info, size); + if (!info) + xtables_error(OTHER_PROBLEM, "Out of memory\n"); + + info->t.u.target_size = size; + info->mr.range[info->mr.rangesize] = *range; + info->mr.rangesize++; + + return info; +} + +/* Ranges expected in network order. */ +static struct xt_entry_target * +parse_to(char *arg, int portok, struct ipt_natinfo *info) +{ + struct nf_nat_range range; + char *colon, *dash, *error; + const struct in_addr *ip; + + memset(&range, 0, sizeof(range)); + colon = strchr(arg, ':'); + + if (colon) { + int port; + + if (!portok) + xtables_error(PARAMETER_PROBLEM, + "Need TCP, UDP, SCTP or DCCP with port specification"); + + range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; + + port = atoi(colon+1); + if (port <= 0 || port > 65535) + xtables_error(PARAMETER_PROBLEM, + "Port `%s' not valid\n", colon+1); + + error = strchr(colon+1, ':'); + if (error) + xtables_error(PARAMETER_PROBLEM, + "Invalid port:port syntax - use dash\n"); + + dash = strchr(colon, '-'); + if (!dash) { + range.min.tcp.port + = range.max.tcp.port + = htons(port); + } else { + int maxport; + + maxport = atoi(dash + 1); + if (maxport <= 0 || maxport > 65535) + xtables_error(PARAMETER_PROBLEM, + "Port `%s' not valid\n", dash+1); + if (maxport < port) + /* People are stupid. */ + xtables_error(PARAMETER_PROBLEM, + "Port range `%s' funky\n", colon+1); + range.min.tcp.port = htons(port); + range.max.tcp.port = htons(maxport); + } + /* Starts with a colon? No IP info...*/ + if (colon == arg) + return &(append_range(info, &range)->t); + *colon = '\0'; + } + + range.flags |= IP_NAT_RANGE_MAP_IPS; + dash = strchr(arg, '-'); + if (colon && dash && dash > colon) + dash = NULL; + + if (dash) + *dash = '\0'; + + ip = xtables_numeric_to_ipaddr(arg); + if (!ip) + xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n", + arg); + range.min_ip = ip->s_addr; + if (dash) { + ip = xtables_numeric_to_ipaddr(dash+1); + if (!ip) + xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n", + dash+1); + range.max_ip = ip->s_addr; + } else + range.max_ip = range.min_ip; + + return &(append_range(info, &range)->t); +} + +static int DNAT_parse(int c, char **argv, int invert, unsigned int *flags, + const void *e, struct xt_entry_target **target) +{ + const struct ipt_entry *entry = e; + struct ipt_natinfo *info = (void *)*target; + int portok; + + if (entry->ip.proto == IPPROTO_TCP + || entry->ip.proto == IPPROTO_UDP + || entry->ip.proto == IPPROTO_SCTP + || entry->ip.proto == IPPROTO_DCCP + || entry->ip.proto == IPPROTO_ICMP) + portok = 1; + else + portok = 0; + + switch (c) { + case '1': + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --to-destination"); + + if (*flags & IPT_DNAT_OPT_DEST) { + if (!kernel_version) + get_kernel_version(); + if (kernel_version > LINUX_VERSION(2, 6, 10)) + xtables_error(PARAMETER_PROBLEM, + "Multiple --to-destination not supported"); + } + *target = parse_to(optarg, portok, info); + /* WTF do we need this for?? */ + if (*flags & IPT_DNAT_OPT_RANDOM) + info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; + *flags |= IPT_DNAT_OPT_DEST; + return 1; + + case '2': + if (*flags & IPT_DNAT_OPT_DEST) { + info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; + *flags |= IPT_DNAT_OPT_RANDOM; + } else + *flags |= IPT_DNAT_OPT_RANDOM; + return 1; + + case '3': + info->mr.range[0].flags |= IP_NAT_RANGE_PERSISTENT; + return 1; + + default: + return 0; + } +} + +static void DNAT_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "You must specify --to-destination"); +} + +static void print_range(const struct nf_nat_range *r) +{ + if (r->flags & IP_NAT_RANGE_MAP_IPS) { + struct in_addr a; + + a.s_addr = r->min_ip; + printf("%s", xtables_ipaddr_to_numeric(&a)); + if (r->max_ip != r->min_ip) { + a.s_addr = r->max_ip; + printf("-%s", xtables_ipaddr_to_numeric(&a)); + } + } + if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) { + printf(":"); + printf("%hu", ntohs(r->min.tcp.port)); + if (r->max.tcp.port != r->min.tcp.port) + printf("-%hu", ntohs(r->max.tcp.port)); + } +} + +static void DNAT_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct ipt_natinfo *info = (const void *)target; + unsigned int i = 0; + + printf("to:"); + for (i = 0; i < info->mr.rangesize; i++) { + print_range(&info->mr.range[i]); + printf(" "); + if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM) + printf("random "); + if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT) + printf("persistent "); + } +} + +static void DNAT_save(const void *ip, const struct xt_entry_target *target) +{ + const struct ipt_natinfo *info = (const void *)target; + unsigned int i = 0; + + for (i = 0; i < info->mr.rangesize; i++) { + printf("--to-destination "); + print_range(&info->mr.range[i]); + printf(" "); + if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM) + printf("--random "); + if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT) + printf("--persistent "); + } +} + +static struct xtables_target dnat_tg_reg = { + .name = "DNAT", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct nf_nat_multi_range)), + .userspacesize = XT_ALIGN(sizeof(struct nf_nat_multi_range)), + .help = DNAT_help, + .parse = DNAT_parse, + .final_check = DNAT_check, + .print = DNAT_print, + .save = DNAT_save, + .extra_opts = DNAT_opts, +}; + +void _init(void) +{ + xtables_register_target(&dnat_tg_reg); +} diff --git a/extensions/libipt_DNAT.man b/extensions/libipt_DNAT.man new file mode 100644 index 0000000..d1e0a3a --- /dev/null +++ b/extensions/libipt_DNAT.man @@ -0,0 +1,39 @@ +This target is only valid in the +.B nat +table, in the +.B PREROUTING +and +.B OUTPUT +chains, and user-defined chains which are only called from those +chains. It specifies that the destination address of the packet +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]] +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 +\fB\-p tcp\fP +or +\fB\-p udp\fP). +If no port range is specified, then the destination port will never be +modified. If no IP address is specified then only the destination port +will be modified. + +In Kernels up to 2.6.10 you can add several \-\-to\-destination options. For +those kernels, if you specify more than one destination address, either via an +address range or multiple \-\-to\-destination options, a simple round-robin (one +after another in cycle) load balancing takes place between these addresses. +Later Kernels (>= 2.6.11-rc1) don't have the ability to NAT to multiple ranges +anymore. +.TP +\fB\-\-random\fP +If option +\fB\-\-random\fP +is used then port mapping will be randomized (kernel >= 2.6.22). +.TP +\fB\-\-persistent\fP +Gives a client the same source-/destination-address for each connection. +This supersedes the SAME target. Support for persistent mappings is available +from 2.6.29-rc2. diff --git a/extensions/libipt_ECN.c b/extensions/libipt_ECN.c new file mode 100644 index 0000000..bf1f8a5 --- /dev/null +++ b/extensions/libipt_ECN.c @@ -0,0 +1,169 @@ +/* Shared library add-on to iptables for ECN, $Version$ + * + * (C) 2002 by Harald Welte <laforge@gnumonks.org> + * + * This program is distributed under the terms of GNU GPL v2, 1991 + * + * libipt_ECN.c borrowed heavily from libipt_DSCP.c + * + * $Id$ + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter_ipv4/ipt_ECN.h> + +static void ECN_help(void) +{ + printf( +"ECN target options\n" +" --ecn-tcp-remove Remove all ECN bits from TCP header\n"); +} + +#if 0 +"ECN target v%s EXPERIMENTAL options (use with extreme care!)\n" +" --ecn-ip-ect Set the IPv4 ECT codepoint (0 to 3)\n" +" --ecn-tcp-cwr Set the IPv4 CWR bit (0 or 1)\n" +" --ecn-tcp-ece Set the IPv4 ECE bit (0 or 1)\n", +#endif + + +static const struct option ECN_opts[] = { + { "ecn-tcp-remove", 0, NULL, 'F' }, + { "ecn-tcp-cwr", 1, NULL, 'G' }, + { "ecn-tcp-ece", 1, NULL, 'H' }, + { "ecn-ip-ect", 1, NULL, '9' }, + { .name = NULL } +}; + +static int ECN_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + unsigned int result; + struct ipt_ECN_info *einfo + = (struct ipt_ECN_info *)(*target)->data; + + switch (c) { + case 'F': + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "ECN target: Only use --ecn-tcp-remove ONCE!"); + einfo->operation = IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR; + einfo->proto.tcp.ece = 0; + einfo->proto.tcp.cwr = 0; + *flags = 1; + break; + case 'G': + if (*flags & IPT_ECN_OP_SET_CWR) + xtables_error(PARAMETER_PROBLEM, + "ECN target: Only use --ecn-tcp-cwr ONCE!"); + if (!xtables_strtoui(optarg, NULL, &result, 0, 1)) + xtables_error(PARAMETER_PROBLEM, + "ECN target: Value out of range"); + einfo->operation |= IPT_ECN_OP_SET_CWR; + einfo->proto.tcp.cwr = result; + *flags |= IPT_ECN_OP_SET_CWR; + break; + case 'H': + if (*flags & IPT_ECN_OP_SET_ECE) + xtables_error(PARAMETER_PROBLEM, + "ECN target: Only use --ecn-tcp-ece ONCE!"); + if (!xtables_strtoui(optarg, NULL, &result, 0, 1)) + xtables_error(PARAMETER_PROBLEM, + "ECN target: Value out of range"); + einfo->operation |= IPT_ECN_OP_SET_ECE; + einfo->proto.tcp.ece = result; + *flags |= IPT_ECN_OP_SET_ECE; + break; + case '9': + if (*flags & IPT_ECN_OP_SET_IP) + xtables_error(PARAMETER_PROBLEM, + "ECN target: Only use --ecn-ip-ect ONCE!"); + if (!xtables_strtoui(optarg, NULL, &result, 0, 3)) + xtables_error(PARAMETER_PROBLEM, + "ECN target: Value out of range"); + einfo->operation |= IPT_ECN_OP_SET_IP; + einfo->ip_ect = result; + *flags |= IPT_ECN_OP_SET_IP; + break; + default: + return 0; + } + + return 1; +} + +static void ECN_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "ECN target: Parameter --ecn-tcp-remove is required"); +} + +static void ECN_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct ipt_ECN_info *einfo = + (const struct ipt_ECN_info *)target->data; + + printf("ECN "); + + if (einfo->operation == (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR) + && einfo->proto.tcp.ece == 0 + && einfo->proto.tcp.cwr == 0) + printf("TCP remove "); + else { + if (einfo->operation & IPT_ECN_OP_SET_ECE) + printf("ECE=%u ", einfo->proto.tcp.ece); + + if (einfo->operation & IPT_ECN_OP_SET_CWR) + printf("CWR=%u ", einfo->proto.tcp.cwr); + + if (einfo->operation & IPT_ECN_OP_SET_IP) + printf("ECT codepoint=%u ", einfo->ip_ect); + } +} + +static void ECN_save(const void *ip, const struct xt_entry_target *target) +{ + const struct ipt_ECN_info *einfo = + (const struct ipt_ECN_info *)target->data; + + if (einfo->operation == (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR) + && einfo->proto.tcp.ece == 0 + && einfo->proto.tcp.cwr == 0) + printf("--ecn-tcp-remove "); + else { + + if (einfo->operation & IPT_ECN_OP_SET_ECE) + printf("--ecn-tcp-ece %d ", einfo->proto.tcp.ece); + + if (einfo->operation & IPT_ECN_OP_SET_CWR) + printf("--ecn-tcp-cwr %d ", einfo->proto.tcp.cwr); + + if (einfo->operation & IPT_ECN_OP_SET_IP) + printf("--ecn-ip-ect %d ", einfo->ip_ect); + } +} + +static struct xtables_target ecn_tg_reg = { + .name = "ECN", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct ipt_ECN_info)), + .userspacesize = XT_ALIGN(sizeof(struct ipt_ECN_info)), + .help = ECN_help, + .parse = ECN_parse, + .final_check = ECN_check, + .print = ECN_print, + .save = ECN_save, + .extra_opts = ECN_opts, +}; + +void _init(void) +{ + xtables_register_target(&ecn_tg_reg); +} diff --git a/extensions/libipt_ECN.man b/extensions/libipt_ECN.man new file mode 100644 index 0000000..a9cbe10 --- /dev/null +++ b/extensions/libipt_ECN.man @@ -0,0 +1,7 @@ +This target allows to selectively work around known ECN blackholes. +It can only be used in the mangle table. +.TP +\fB\-\-ecn\-tcp\-remove\fP +Remove all ECN bits from the TCP header. Of course, it can only be used +in conjunction with +\fB\-p tcp\fP. diff --git a/extensions/libipt_LOG.c b/extensions/libipt_LOG.c new file mode 100644 index 0000000..9afb91d --- /dev/null +++ b/extensions/libipt_LOG.c @@ -0,0 +1,264 @@ +/* Shared library add-on to iptables to add LOG support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> +#include <getopt.h> +#include <xtables.h> +#include <linux/netfilter_ipv4/ipt_LOG.h> + +#define LOG_DEFAULT_LEVEL LOG_WARNING + +#ifndef IPT_LOG_UID /* Old kernel */ +#define IPT_LOG_UID 0x08 /* Log UID owning local socket */ +#undef IPT_LOG_MASK +#define IPT_LOG_MASK 0x0f +#endif + +static void LOG_help(void) +{ + printf( +"LOG target options:\n" +" --log-level level Level of logging (numeric or see syslog.conf)\n" +" --log-prefix prefix Prefix log messages with this prefix.\n\n" +" --log-tcp-sequence Log TCP sequence numbers.\n\n" +" --log-tcp-options Log TCP options.\n\n" +" --log-ip-options Log IP options.\n\n" +" --log-uid Log UID owning the local socket.\n\n"); +} + +static const struct option LOG_opts[] = { + { .name = "log-level", .has_arg = 1, .val = '!' }, + { .name = "log-prefix", .has_arg = 1, .val = '#' }, + { .name = "log-tcp-sequence", .has_arg = 0, .val = '1' }, + { .name = "log-tcp-options", .has_arg = 0, .val = '2' }, + { .name = "log-ip-options", .has_arg = 0, .val = '3' }, + { .name = "log-uid", .has_arg = 0, .val = '4' }, + { .name = NULL } +}; + +static void LOG_init(struct xt_entry_target *t) +{ + struct ipt_log_info *loginfo = (struct ipt_log_info *)t->data; + + loginfo->level = LOG_DEFAULT_LEVEL; + +} + +struct ipt_log_names { + const char *name; + unsigned int level; +}; + +static const struct ipt_log_names ipt_log_names[] += { { .name = "alert", .level = LOG_ALERT }, + { .name = "crit", .level = LOG_CRIT }, + { .name = "debug", .level = LOG_DEBUG }, + { .name = "emerg", .level = LOG_EMERG }, + { .name = "error", .level = LOG_ERR }, /* DEPRECATED */ + { .name = "info", .level = LOG_INFO }, + { .name = "notice", .level = LOG_NOTICE }, + { .name = "panic", .level = LOG_EMERG }, /* DEPRECATED */ + { .name = "warning", .level = LOG_WARNING } +}; + +static u_int8_t +parse_level(const char *level) +{ + unsigned int lev = -1; + unsigned int set = 0; + + if (!xtables_strtoui(level, NULL, &lev, 0, 7)) { + unsigned int i = 0; + + for (i = 0; i < ARRAY_SIZE(ipt_log_names); ++i) + if (strncasecmp(level, ipt_log_names[i].name, + strlen(level)) == 0) { + if (set++) + xtables_error(PARAMETER_PROBLEM, + "log-level `%s' ambiguous", + level); + lev = ipt_log_names[i].level; + } + + if (!set) + xtables_error(PARAMETER_PROBLEM, + "log-level `%s' unknown", level); + } + + return lev; +} + +#define IPT_LOG_OPT_LEVEL 0x01 +#define IPT_LOG_OPT_PREFIX 0x02 +#define IPT_LOG_OPT_TCPSEQ 0x04 +#define IPT_LOG_OPT_TCPOPT 0x08 +#define IPT_LOG_OPT_IPOPT 0x10 +#define IPT_LOG_OPT_UID 0x20 + +static int LOG_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct ipt_log_info *loginfo = (struct ipt_log_info *)(*target)->data; + + switch (c) { + case '!': + if (*flags & IPT_LOG_OPT_LEVEL) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --log-level twice"); + + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --log-level"); + + loginfo->level = parse_level(optarg); + *flags |= IPT_LOG_OPT_LEVEL; + break; + + case '#': + if (*flags & IPT_LOG_OPT_PREFIX) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --log-prefix twice"); + + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --log-prefix"); + + if (strlen(optarg) > sizeof(loginfo->prefix) - 1) + xtables_error(PARAMETER_PROBLEM, + "Maximum prefix length %u for --log-prefix", + (unsigned int)sizeof(loginfo->prefix) - 1); + + if (strlen(optarg) == 0) + xtables_error(PARAMETER_PROBLEM, + "No prefix specified for --log-prefix"); + + if (strlen(optarg) != strlen(strtok(optarg, "\n"))) + xtables_error(PARAMETER_PROBLEM, + "Newlines not allowed in --log-prefix"); + + strcpy(loginfo->prefix, optarg); + *flags |= IPT_LOG_OPT_PREFIX; + break; + + case '1': + if (*flags & IPT_LOG_OPT_TCPSEQ) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --log-tcp-sequence " + "twice"); + + loginfo->logflags |= IPT_LOG_TCPSEQ; + *flags |= IPT_LOG_OPT_TCPSEQ; + break; + + case '2': + if (*flags & IPT_LOG_OPT_TCPOPT) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --log-tcp-options twice"); + + loginfo->logflags |= IPT_LOG_TCPOPT; + *flags |= IPT_LOG_OPT_TCPOPT; + break; + + case '3': + if (*flags & IPT_LOG_OPT_IPOPT) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --log-ip-options twice"); + + loginfo->logflags |= IPT_LOG_IPOPT; + *flags |= IPT_LOG_OPT_IPOPT; + break; + + case '4': + if (*flags & IPT_LOG_OPT_UID) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --log-uid twice"); + + loginfo->logflags |= IPT_LOG_UID; + *flags |= IPT_LOG_OPT_UID; + break; + + default: + return 0; + } + + return 1; +} + +static void LOG_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct ipt_log_info *loginfo + = (const struct ipt_log_info *)target->data; + unsigned int i = 0; + + printf("LOG "); + if (numeric) + printf("flags %u level %u ", + loginfo->logflags, loginfo->level); + else { + for (i = 0; i < ARRAY_SIZE(ipt_log_names); ++i) + if (loginfo->level == ipt_log_names[i].level) { + printf("level %s ", ipt_log_names[i].name); + break; + } + if (i == ARRAY_SIZE(ipt_log_names)) + printf("UNKNOWN level %u ", loginfo->level); + if (loginfo->logflags & IPT_LOG_TCPSEQ) + printf("tcp-sequence "); + if (loginfo->logflags & IPT_LOG_TCPOPT) + printf("tcp-options "); + if (loginfo->logflags & IPT_LOG_IPOPT) + printf("ip-options "); + if (loginfo->logflags & IPT_LOG_UID) + printf("uid "); + if (loginfo->logflags & ~(IPT_LOG_MASK)) + printf("unknown-flags "); + } + + if (strcmp(loginfo->prefix, "") != 0) + printf("prefix `%s' ", loginfo->prefix); +} + +static void LOG_save(const void *ip, const struct xt_entry_target *target) +{ + const struct ipt_log_info *loginfo + = (const struct ipt_log_info *)target->data; + + if (strcmp(loginfo->prefix, "") != 0) { + printf("--log-prefix "); + xtables_save_string(loginfo->prefix); + } + + if (loginfo->level != LOG_DEFAULT_LEVEL) + printf("--log-level %d ", loginfo->level); + + if (loginfo->logflags & IPT_LOG_TCPSEQ) + printf("--log-tcp-sequence "); + if (loginfo->logflags & IPT_LOG_TCPOPT) + printf("--log-tcp-options "); + if (loginfo->logflags & IPT_LOG_IPOPT) + printf("--log-ip-options "); + if (loginfo->logflags & IPT_LOG_UID) + printf("--log-uid "); +} + +static struct xtables_target log_tg_reg = { + .name = "LOG", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct ipt_log_info)), + .userspacesize = XT_ALIGN(sizeof(struct ipt_log_info)), + .help = LOG_help, + .init = LOG_init, + .parse = LOG_parse, + .print = LOG_print, + .save = LOG_save, + .extra_opts = LOG_opts, +}; + +void _init(void) +{ + xtables_register_target(&log_tg_reg); +} diff --git a/extensions/libipt_LOG.man b/extensions/libipt_LOG.man new file mode 100644 index 0000000..47c35e0 --- /dev/null +++ b/extensions/libipt_LOG.man @@ -0,0 +1,31 @@ +Turn on kernel logging of matching packets. When this option is set +for a rule, the Linux kernel will print some information on all +matching packets (like most IP header fields) via the kernel log +(where it can be read with +.I dmesg +or +.IR syslogd (8)). +This is a "non-terminating target", i.e. rule traversal continues at +the next rule. So if you want to LOG the packets you refuse, use two +separate rules with the same matching criteria, first using target LOG +then DROP (or REJECT). +.TP +\fB\-\-log\-level\fP \fIlevel\fP +Level of logging (numeric or see \fIsyslog.conf\fP(5)). +.TP +\fB\-\-log\-prefix\fP \fIprefix\fP +Prefix log messages with the specified prefix; up to 29 letters long, +and useful for distinguishing messages in the logs. +.TP +\fB\-\-log\-tcp\-sequence\fP +Log TCP sequence numbers. This is a security risk if the log is +readable by users. +.TP +\fB\-\-log\-tcp\-options\fP +Log options from the TCP packet header. +.TP +\fB\-\-log\-ip\-options\fP +Log options from the IP packet header. +.TP +\fB\-\-log\-uid\fP +Log the userid of the process which generated the packet. diff --git a/extensions/libipt_MASQUERADE.c b/extensions/libipt_MASQUERADE.c new file mode 100644 index 0000000..3386ff3 --- /dev/null +++ b/extensions/libipt_MASQUERADE.c @@ -0,0 +1,163 @@ +/* Shared library add-on to iptables to add masquerade support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <limits.h> /* INT_MAX in ip_tables.h */ +#include <linux/netfilter_ipv4/ip_tables.h> +#include <net/netfilter/nf_nat.h> + +static void MASQUERADE_help(void) +{ + printf( +"MASQUERADE target options:\n" +" --to-ports <port>[-<port>]\n" +" Port (range) to map to.\n" +" --random\n" +" Randomize source port.\n"); +} + +static const struct option MASQUERADE_opts[] = { + { "to-ports", 1, NULL, '1' }, + { "random", 0, NULL, '2' }, + { .name = NULL } +}; + +static void MASQUERADE_init(struct xt_entry_target *t) +{ + struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *)t->data; + + /* Actually, it's 0, but it's ignored at the moment. */ + mr->rangesize = 1; + +} + +/* Parses ports */ +static void +parse_ports(const char *arg, struct nf_nat_multi_range *mr) +{ + char *end; + unsigned int port, maxport; + + mr->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; + + if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX)) + xtables_param_act(XTF_BAD_VALUE, "MASQUERADE", "--to-ports", arg); + + switch (*end) { + case '\0': + mr->range[0].min.tcp.port + = mr->range[0].max.tcp.port + = htons(port); + return; + case '-': + if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX)) + break; + + if (maxport < port) + break; + + mr->range[0].min.tcp.port = htons(port); + mr->range[0].max.tcp.port = htons(maxport); + return; + default: + break; + } + xtables_param_act(XTF_BAD_VALUE, "MASQUERADE", "--to-ports", arg); +} + +static int MASQUERADE_parse(int c, char **argv, int invert, unsigned int *flags, + const void *e, struct xt_entry_target **target) +{ + const struct ipt_entry *entry = e; + int portok; + struct nf_nat_multi_range *mr + = (struct nf_nat_multi_range *)(*target)->data; + + if (entry->ip.proto == IPPROTO_TCP + || entry->ip.proto == IPPROTO_UDP + || entry->ip.proto == IPPROTO_SCTP + || entry->ip.proto == IPPROTO_DCCP + || entry->ip.proto == IPPROTO_ICMP) + portok = 1; + else + portok = 0; + + switch (c) { + case '1': + if (!portok) + xtables_error(PARAMETER_PROBLEM, + "Need TCP, UDP, SCTP or DCCP with port specification"); + + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --to-ports"); + + parse_ports(optarg, mr); + return 1; + + case '2': + mr->range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; + return 1; + + default: + return 0; + } +} + +static void +MASQUERADE_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct nf_nat_multi_range *mr = (const void *)target->data; + const struct nf_nat_range *r = &mr->range[0]; + + if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) { + printf("masq ports: "); + printf("%hu", ntohs(r->min.tcp.port)); + if (r->max.tcp.port != r->min.tcp.port) + printf("-%hu", ntohs(r->max.tcp.port)); + printf(" "); + } + + if (r->flags & IP_NAT_RANGE_PROTO_RANDOM) + printf("random "); +} + +static void +MASQUERADE_save(const void *ip, const struct xt_entry_target *target) +{ + const struct nf_nat_multi_range *mr = (const void *)target->data; + const struct nf_nat_range *r = &mr->range[0]; + + if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) { + printf("--to-ports %hu", ntohs(r->min.tcp.port)); + if (r->max.tcp.port != r->min.tcp.port) + printf("-%hu", ntohs(r->max.tcp.port)); + printf(" "); + } + + if (r->flags & IP_NAT_RANGE_PROTO_RANDOM) + printf("--random "); +} + +static struct xtables_target masquerade_tg_reg = { + .name = "MASQUERADE", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct nf_nat_multi_range)), + .userspacesize = XT_ALIGN(sizeof(struct nf_nat_multi_range)), + .help = MASQUERADE_help, + .init = MASQUERADE_init, + .parse = MASQUERADE_parse, + .print = MASQUERADE_print, + .save = MASQUERADE_save, + .extra_opts = MASQUERADE_opts, +}; + +void _init(void) +{ + xtables_register_target(&masquerade_tg_reg); +} diff --git a/extensions/libipt_MASQUERADE.man b/extensions/libipt_MASQUERADE.man new file mode 100644 index 0000000..8f42993 --- /dev/null +++ b/extensions/libipt_MASQUERADE.man @@ -0,0 +1,30 @@ +This target is only valid in the +.B nat +table, in the +.B POSTROUTING +chain. It should only be used with dynamically assigned IP (dialup) +connections: if you have a static IP address, you should use the SNAT +target. Masquerading is equivalent to specifying a mapping to the IP +address of the interface the packet is going out, but also has the +effect that connections are +.I forgotten +when the interface goes down. This is the correct behavior when the +next dialup is unlikely to have the same interface address (and hence +any established connections are lost anyway). It takes one option: +.TP +\fB\-\-to\-ports\fP \fIport\fP[\fB\-\fP\fIport\fP] +This specifies a range of source ports to use, overriding the default +.B SNAT +source port-selection heuristics (see above). This is only valid +if the rule also specifies +\fB\-p tcp\fP +or +\fB\-p udp\fP. +.TP +\fB\-\-random\fP +Randomize source port mapping +If option +\fB\-\-random\fP +is used then port mapping will be randomized (kernel >= 2.6.21). +.RS +.PP diff --git a/extensions/libipt_MIRROR.c b/extensions/libipt_MIRROR.c new file mode 100644 index 0000000..fb78751 --- /dev/null +++ b/extensions/libipt_MIRROR.c @@ -0,0 +1,15 @@ +/* Shared library add-on to iptables to add MIRROR target support. */ +#include <xtables.h> + +static struct xtables_target mirror_tg_reg = { + .name = "MIRROR", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(0), + .userspacesize = XT_ALIGN(0), +}; + +void _init(void) +{ + xtables_register_target(&mirror_tg_reg); +} diff --git a/extensions/libipt_MIRROR.man b/extensions/libipt_MIRROR.man new file mode 100644 index 0000000..7b720bc --- /dev/null +++ b/extensions/libipt_MIRROR.man @@ -0,0 +1,12 @@ +This is an experimental demonstration target which inverts the source +and destination fields in the IP header and retransmits the packet. +It is only valid in the +.BR INPUT , +.B FORWARD +and +.B PREROUTING +chains, and user-defined chains which are only called from those +chains. Note that the outgoing packets are +.B NOT +seen by any packet filtering chains, connection tracking or NAT, to +avoid loops and other problems. diff --git a/extensions/libipt_NETMAP.c b/extensions/libipt_NETMAP.c new file mode 100644 index 0000000..b05022b --- /dev/null +++ b/extensions/libipt_NETMAP.c @@ -0,0 +1,182 @@ +/* Shared library add-on to iptables to add static NAT support. + Author: Svenning Soerensen <svenning@post5.tele.dk> +*/ + +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <net/netfilter/nf_nat.h> + +#define MODULENAME "NETMAP" + +static const struct option NETMAP_opts[] = { + { "to", 1, NULL, '1' }, + { .name = NULL } +}; + +static void NETMAP_help(void) +{ + printf(MODULENAME" target options:\n" + " --%s address[/mask]\n" + " Network address to map to.\n\n", + NETMAP_opts[0].name); +} + +static u_int32_t +bits2netmask(int bits) +{ + u_int32_t netmask, bm; + + if (bits >= 32 || bits < 0) + return(~0); + for (netmask = 0, bm = 0x80000000; bits; bits--, bm >>= 1) + netmask |= bm; + return htonl(netmask); +} + +static int +netmask2bits(u_int32_t netmask) +{ + u_int32_t bm; + int bits; + + netmask = ntohl(netmask); + for (bits = 0, bm = 0x80000000; netmask & bm; netmask <<= 1) + bits++; + if (netmask) + return -1; /* holes in netmask */ + return bits; +} + +static void NETMAP_init(struct xt_entry_target *t) +{ + struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *)t->data; + + /* Actually, it's 0, but it's ignored at the moment. */ + mr->rangesize = 1; + +} + +/* Parses network address */ +static void +parse_to(char *arg, struct nf_nat_range *range) +{ + char *slash; + const struct in_addr *ip; + u_int32_t netmask; + unsigned int bits; + + range->flags |= IP_NAT_RANGE_MAP_IPS; + slash = strchr(arg, '/'); + if (slash) + *slash = '\0'; + + ip = xtables_numeric_to_ipaddr(arg); + if (!ip) + xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n", + arg); + range->min_ip = ip->s_addr; + if (slash) { + if (strchr(slash+1, '.')) { + ip = xtables_numeric_to_ipmask(slash+1); + if (!ip) + xtables_error(PARAMETER_PROBLEM, "Bad netmask \"%s\"\n", + slash+1); + netmask = ip->s_addr; + } + else { + if (!xtables_strtoui(slash+1, NULL, &bits, 0, 32)) + xtables_error(PARAMETER_PROBLEM, "Bad netmask \"%s\"\n", + slash+1); + netmask = bits2netmask(bits); + } + /* Don't allow /0 (/1 is probably insane, too) */ + if (netmask == 0) + xtables_error(PARAMETER_PROBLEM, "Netmask needed\n"); + } + else + netmask = ~0; + + if (range->min_ip & ~netmask) { + if (slash) + *slash = '/'; + xtables_error(PARAMETER_PROBLEM, "Bad network address \"%s\"\n", + arg); + } + range->max_ip = range->min_ip | ~netmask; +} + +static int NETMAP_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct nf_nat_multi_range *mr + = (struct nf_nat_multi_range *)(*target)->data; + + switch (c) { + case '1': + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --%s", NETMAP_opts[0].name); + + parse_to(optarg, &mr->range[0]); + *flags = 1; + return 1; + + default: + return 0; + } +} + +static void NETMAP_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + MODULENAME" needs --%s", NETMAP_opts[0].name); +} + +static void NETMAP_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct nf_nat_multi_range *mr = (const void *)target->data; + const struct nf_nat_range *r = &mr->range[0]; + struct in_addr a; + int bits; + + a.s_addr = r->min_ip; + printf("%s", xtables_ipaddr_to_numeric(&a)); + a.s_addr = ~(r->min_ip ^ r->max_ip); + bits = netmask2bits(a.s_addr); + if (bits < 0) + printf("/%s", xtables_ipaddr_to_numeric(&a)); + else + printf("/%d", bits); +} + +static void NETMAP_save(const void *ip, const struct xt_entry_target *target) +{ + printf("--%s ", NETMAP_opts[0].name); + NETMAP_print(ip, target, 0); +} + +static struct xtables_target netmap_tg_reg = { + .name = MODULENAME, + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct nf_nat_multi_range)), + .userspacesize = XT_ALIGN(sizeof(struct nf_nat_multi_range)), + .help = NETMAP_help, + .init = NETMAP_init, + .parse = NETMAP_parse, + .final_check = NETMAP_check, + .print = NETMAP_print, + .save = NETMAP_save, + .extra_opts = NETMAP_opts, +}; + +void _init(void) +{ + xtables_register_target(&netmap_tg_reg); +} diff --git a/extensions/libipt_NETMAP.man b/extensions/libipt_NETMAP.man new file mode 100644 index 0000000..a7e90b8 --- /dev/null +++ b/extensions/libipt_NETMAP.man @@ -0,0 +1,9 @@ +This target allows you to statically map a whole network of addresses onto +another network of addresses. It can only be used from rules in the +.B nat +table. +.TP +\fB\-\-to\fP \fIaddress\fP[\fB/\fP\fImask\fP] +Network address to map to. The resulting address will be constructed in the +following way: All 'one' bits in the mask are filled in from the new `address'. +All bits that are zero in the mask are filled in from the original address. diff --git a/extensions/libipt_REDIRECT.c b/extensions/libipt_REDIRECT.c new file mode 100644 index 0000000..324d0eb --- /dev/null +++ b/extensions/libipt_REDIRECT.c @@ -0,0 +1,171 @@ +/* Shared library add-on to iptables to add redirect support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <limits.h> /* INT_MAX in ip_tables.h */ +#include <linux/netfilter_ipv4/ip_tables.h> +#include <net/netfilter/nf_nat.h> + +#define IPT_REDIRECT_OPT_DEST 0x01 +#define IPT_REDIRECT_OPT_RANDOM 0x02 + +static void REDIRECT_help(void) +{ + printf( +"REDIRECT target options:\n" +" --to-ports <port>[-<port>]\n" +" Port (range) to map to.\n" +" [--random]\n"); +} + +static const struct option REDIRECT_opts[] = { + { "to-ports", 1, NULL, '1' }, + { "random", 0, NULL, '2' }, + { .name = NULL } +}; + +static void REDIRECT_init(struct xt_entry_target *t) +{ + struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *)t->data; + + /* Actually, it's 0, but it's ignored at the moment. */ + mr->rangesize = 1; + +} + +/* Parses ports */ +static void +parse_ports(const char *arg, struct nf_nat_multi_range *mr) +{ + char *end; + unsigned int port, maxport; + + mr->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; + + if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX) && + (port = xtables_service_to_port(arg, NULL)) == (unsigned)-1) + xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg); + + switch (*end) { + case '\0': + mr->range[0].min.tcp.port + = mr->range[0].max.tcp.port + = htons(port); + return; + case '-': + if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX) && + (maxport = xtables_service_to_port(end + 1, NULL)) == (unsigned)-1) + break; + + if (maxport < port) + break; + + mr->range[0].min.tcp.port = htons(port); + mr->range[0].max.tcp.port = htons(maxport); + return; + default: + break; + } + xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg); +} + +static int REDIRECT_parse(int c, char **argv, int invert, unsigned int *flags, + const void *e, struct xt_entry_target **target) +{ + const struct ipt_entry *entry = e; + struct nf_nat_multi_range *mr + = (struct nf_nat_multi_range *)(*target)->data; + int portok; + + if (entry->ip.proto == IPPROTO_TCP + || entry->ip.proto == IPPROTO_UDP + || entry->ip.proto == IPPROTO_SCTP + || entry->ip.proto == IPPROTO_DCCP + || entry->ip.proto == IPPROTO_ICMP) + portok = 1; + else + portok = 0; + + switch (c) { + case '1': + if (!portok) + xtables_error(PARAMETER_PROBLEM, + "Need TCP, UDP, SCTP or DCCP with port specification"); + + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --to-ports"); + + parse_ports(optarg, mr); + if (*flags & IPT_REDIRECT_OPT_RANDOM) + mr->range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; + *flags |= IPT_REDIRECT_OPT_DEST; + return 1; + + case '2': + if (*flags & IPT_REDIRECT_OPT_DEST) { + mr->range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; + *flags |= IPT_REDIRECT_OPT_RANDOM; + } else + *flags |= IPT_REDIRECT_OPT_RANDOM; + return 1; + + default: + return 0; + } +} + +static void REDIRECT_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct nf_nat_multi_range *mr = (const void *)target->data; + const struct nf_nat_range *r = &mr->range[0]; + + if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) { + printf("redir ports "); + printf("%hu", ntohs(r->min.tcp.port)); + if (r->max.tcp.port != r->min.tcp.port) + printf("-%hu", ntohs(r->max.tcp.port)); + printf(" "); + if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM) + printf("random "); + } +} + +static void REDIRECT_save(const void *ip, const struct xt_entry_target *target) +{ + const struct nf_nat_multi_range *mr = (const void *)target->data; + const struct nf_nat_range *r = &mr->range[0]; + + if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) { + printf("--to-ports "); + printf("%hu", ntohs(r->min.tcp.port)); + if (r->max.tcp.port != r->min.tcp.port) + printf("-%hu", ntohs(r->max.tcp.port)); + printf(" "); + if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM) + printf("--random "); + } +} + +static struct xtables_target redirect_tg_reg = { + .name = "REDIRECT", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct nf_nat_multi_range)), + .userspacesize = XT_ALIGN(sizeof(struct nf_nat_multi_range)), + .help = REDIRECT_help, + .init = REDIRECT_init, + .parse = REDIRECT_parse, + .print = REDIRECT_print, + .save = REDIRECT_save, + .extra_opts = REDIRECT_opts, +}; + +void _init(void) +{ + xtables_register_target(&redirect_tg_reg); +} diff --git a/extensions/libipt_REDIRECT.man b/extensions/libipt_REDIRECT.man new file mode 100644 index 0000000..90ab19d --- /dev/null +++ b/extensions/libipt_REDIRECT.man @@ -0,0 +1,25 @@ +This target is only valid in the +.B nat +table, in the +.B PREROUTING +and +.B OUTPUT +chains, and user-defined chains which are only called from those +chains. It redirects the packet to the machine itself by changing the +destination IP to the primary address of the incoming interface +(locally-generated packets are mapped to the 127.0.0.1 address). +.TP +\fB\-\-to\-ports\fP \fIport\fP[\fB\-\fP\fIport\fP] +This specifies a destination port or range of ports to use: without +this, the destination port is never altered. This is only valid +if the rule also specifies +\fB\-p tcp\fP +or +\fB\-p udp\fP. +.TP +\fB\-\-random\fP +If option +\fB\-\-random\fP +is used then port mapping will be randomized (kernel >= 2.6.22). +.RS +.PP diff --git a/extensions/libipt_REJECT.c b/extensions/libipt_REJECT.c new file mode 100644 index 0000000..85d9e53 --- /dev/null +++ b/extensions/libipt_REJECT.c @@ -0,0 +1,167 @@ +/* Shared library add-on to iptables to add customized REJECT support. + * + * (C) 2000 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <linux/netfilter_ipv4/ipt_REJECT.h> +#include <linux/version.h> + +/* If we are compiling against a kernel that does not support + * IPT_ICMP_ADMIN_PROHIBITED, we are emulating it. + * The result will be a plain DROP of the packet instead of + * reject. -- Maciej Soltysiak <solt@dns.toxicfilms.tv> + */ +#ifndef IPT_ICMP_ADMIN_PROHIBITED +#define IPT_ICMP_ADMIN_PROHIBITED IPT_TCP_RESET + 1 +#endif + +struct reject_names { + const char *name; + const char *alias; + enum ipt_reject_with with; + const char *desc; +}; + +static const struct reject_names reject_table[] = { + {"icmp-net-unreachable", "net-unreach", + IPT_ICMP_NET_UNREACHABLE, "ICMP network unreachable"}, + {"icmp-host-unreachable", "host-unreach", + IPT_ICMP_HOST_UNREACHABLE, "ICMP host unreachable"}, + {"icmp-proto-unreachable", "proto-unreach", + IPT_ICMP_PROT_UNREACHABLE, "ICMP protocol unreachable"}, + {"icmp-port-unreachable", "port-unreach", + IPT_ICMP_PORT_UNREACHABLE, "ICMP port unreachable (default)"}, +#if 0 + {"echo-reply", "echoreply", + IPT_ICMP_ECHOREPLY, "for ICMP echo only: faked ICMP echo reply"}, +#endif + {"icmp-net-prohibited", "net-prohib", + IPT_ICMP_NET_PROHIBITED, "ICMP network prohibited"}, + {"icmp-host-prohibited", "host-prohib", + IPT_ICMP_HOST_PROHIBITED, "ICMP host prohibited"}, + {"tcp-reset", "tcp-rst", + IPT_TCP_RESET, "TCP RST packet"}, + {"icmp-admin-prohibited", "admin-prohib", + IPT_ICMP_ADMIN_PROHIBITED, "ICMP administratively prohibited (*)"} +}; + +static void +print_reject_types(void) +{ + unsigned int i; + + printf("Valid reject types:\n"); + + for (i = 0; i < ARRAY_SIZE(reject_table); ++i) { + printf(" %-25s\t%s\n", reject_table[i].name, reject_table[i].desc); + printf(" %-25s\talias\n", reject_table[i].alias); + } + printf("\n"); +} + +static void REJECT_help(void) +{ + printf( +"REJECT target options:\n" +"--reject-with type drop input packet and send back\n" +" a reply packet according to type:\n"); + + print_reject_types(); + + printf("(*) See man page or read the INCOMPATIBILITES file for compatibility issues.\n"); +} + +static const struct option REJECT_opts[] = { + { "reject-with", 1, NULL, '1' }, + { .name = NULL } +}; + +static void REJECT_init(struct xt_entry_target *t) +{ + struct ipt_reject_info *reject = (struct ipt_reject_info *)t->data; + + /* default */ + reject->with = IPT_ICMP_PORT_UNREACHABLE; + +} + +static int REJECT_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct ipt_reject_info *reject = (struct ipt_reject_info *)(*target)->data; + static const unsigned int limit = ARRAY_SIZE(reject_table); + unsigned int i; + + switch(c) { + case '1': + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --reject-with"); + for (i = 0; i < limit; i++) { + if ((strncasecmp(reject_table[i].name, optarg, strlen(optarg)) == 0) + || (strncasecmp(reject_table[i].alias, optarg, strlen(optarg)) == 0)) { + reject->with = reject_table[i].with; + return 1; + } + } + /* This due to be dropped late in 2.4 pre-release cycle --RR */ + if (strncasecmp("echo-reply", optarg, strlen(optarg)) == 0 + || strncasecmp("echoreply", optarg, strlen(optarg)) == 0) + fprintf(stderr, "--reject-with echo-reply no longer" + " supported\n"); + xtables_error(PARAMETER_PROBLEM, "unknown reject type \"%s\"", optarg); + default: + /* Fall through */ + break; + } + return 0; +} + +static void REJECT_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct ipt_reject_info *reject + = (const struct ipt_reject_info *)target->data; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(reject_table); ++i) + if (reject_table[i].with == reject->with) + break; + printf("reject-with %s ", reject_table[i].name); +} + +static void REJECT_save(const void *ip, const struct xt_entry_target *target) +{ + const struct ipt_reject_info *reject + = (const struct ipt_reject_info *)target->data; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(reject_table); ++i) + if (reject_table[i].with == reject->with) + break; + + printf("--reject-with %s ", reject_table[i].name); +} + +static struct xtables_target reject_tg_reg = { + .name = "REJECT", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct ipt_reject_info)), + .userspacesize = XT_ALIGN(sizeof(struct ipt_reject_info)), + .help = REJECT_help, + .init = REJECT_init, + .parse = REJECT_parse, + .print = REJECT_print, + .save = REJECT_save, + .extra_opts = REJECT_opts, +}; + +void _init(void) +{ + xtables_register_target(&reject_tg_reg); +} diff --git a/extensions/libipt_REJECT.man b/extensions/libipt_REJECT.man new file mode 100644 index 0000000..c419a85 --- /dev/null +++ b/extensions/libipt_REJECT.man @@ -0,0 +1,32 @@ +This is used to send back an error packet in response to the matched +packet: otherwise it is equivalent to +.B DROP +so it is a terminating TARGET, ending rule traversal. +This target is only valid in the +.BR INPUT , +.B FORWARD +and +.B OUTPUT +chains, and user-defined chains which are only called from those +chains. The following option controls the nature of the error packet +returned: +.TP +\fB\-\-reject\-with\fP \fItype\fP +The type given can be +\fBicmp\-net\-unreachable\fP, +\fBicmp\-host\-unreachable\fP, +\fBicmp\-port\-unreachable\fP, +\fBicmp\-proto\-unreachable\fP, +\fBicmp\-net\-prohibited\fP, +\fBicmp\-host\-prohibited\fP or +\fBicmp\-admin\-prohibited\fP (*) +which return the appropriate ICMP error message (\fBport\-unreachable\fP is +the default). The option +\fBtcp\-reset\fP +can be used on rules which only match the TCP protocol: this causes a +TCP RST packet to be sent back. This is mainly useful for blocking +.I ident +(113/tcp) probes which frequently occur when sending mail to broken mail +hosts (which won't accept your mail otherwise). +.PP +(*) Using icmp\-admin\-prohibited with kernels that do not support it will result in a plain DROP instead of REJECT diff --git a/extensions/libipt_SAME.c b/extensions/libipt_SAME.c new file mode 100644 index 0000000..ed02ef9 --- /dev/null +++ b/extensions/libipt_SAME.c @@ -0,0 +1,217 @@ +/* Shared library add-on to iptables to add simple non load-balancing SNAT support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <net/netfilter/nf_nat.h> +/* For 64bit kernel / 32bit userspace */ +#include <linux/netfilter_ipv4/ipt_SAME.h> + +static void SAME_help(void) +{ + printf( +"SAME target options:\n" +" --to <ipaddr>-<ipaddr>\n" +" Addresses to map source to.\n" +" May be specified more than\n" +" once for multiple ranges.\n" +" --nodst\n" +" Don't use destination-ip in\n" +" source selection\n" +" --random\n" +" Randomize source port\n"); +} + +static const struct option SAME_opts[] = { + { "to", 1, NULL, '1' }, + { "nodst", 0, NULL, '2'}, + { "random", 0, NULL, '3' }, + { .name = NULL } +}; + +static void SAME_init(struct xt_entry_target *t) +{ + struct ipt_same_info *mr = (struct ipt_same_info *)t->data; + + /* Set default to 0 */ + mr->rangesize = 0; + mr->info = 0; + mr->ipnum = 0; + +} + +/* Parses range of IPs */ +static void +parse_to(char *arg, struct nf_nat_range *range) +{ + char *dash; + const struct in_addr *ip; + + range->flags |= IP_NAT_RANGE_MAP_IPS; + dash = strchr(arg, '-'); + + if (dash) + *dash = '\0'; + + ip = xtables_numeric_to_ipaddr(arg); + if (!ip) + xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n", + arg); + range->min_ip = ip->s_addr; + + if (dash) { + ip = xtables_numeric_to_ipaddr(dash+1); + if (!ip) + xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n", + dash+1); + } + range->max_ip = ip->s_addr; + if (dash) + if (range->min_ip > range->max_ip) + xtables_error(PARAMETER_PROBLEM, "Bad IP range \"%s-%s\"\n", + arg, dash+1); +} + +#define IPT_SAME_OPT_TO 0x01 +#define IPT_SAME_OPT_NODST 0x02 +#define IPT_SAME_OPT_RANDOM 0x04 + +static int SAME_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct ipt_same_info *mr + = (struct ipt_same_info *)(*target)->data; + unsigned int count; + + switch (c) { + case '1': + if (mr->rangesize == IPT_SAME_MAX_RANGE) + xtables_error(PARAMETER_PROBLEM, + "Too many ranges specified, maximum " + "is %i ranges.\n", + IPT_SAME_MAX_RANGE); + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --to"); + + parse_to(optarg, &mr->range[mr->rangesize]); + /* WTF do we need this for? */ + if (*flags & IPT_SAME_OPT_RANDOM) + mr->range[mr->rangesize].flags + |= IP_NAT_RANGE_PROTO_RANDOM; + mr->rangesize++; + *flags |= IPT_SAME_OPT_TO; + break; + + case '2': + if (*flags & IPT_SAME_OPT_NODST) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --nodst twice"); + + mr->info |= IPT_SAME_NODST; + *flags |= IPT_SAME_OPT_NODST; + break; + + case '3': + *flags |= IPT_SAME_OPT_RANDOM; + for (count=0; count < mr->rangesize; count++) + mr->range[count].flags |= IP_NAT_RANGE_PROTO_RANDOM; + break; + + default: + return 0; + } + + return 1; +} + +static void SAME_check(unsigned int flags) +{ + if (!(flags & IPT_SAME_OPT_TO)) + xtables_error(PARAMETER_PROBLEM, + "SAME needs --to"); +} + +static void SAME_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + unsigned int count; + const struct ipt_same_info *mr = (const void *)target->data; + int random_selection = 0; + + printf("same:"); + + for (count = 0; count < mr->rangesize; count++) { + const struct nf_nat_range *r = &mr->range[count]; + struct in_addr a; + + a.s_addr = r->min_ip; + + printf("%s", xtables_ipaddr_to_numeric(&a)); + a.s_addr = r->max_ip; + + if (r->min_ip == r->max_ip) + printf(" "); + else + printf("-%s ", xtables_ipaddr_to_numeric(&a)); + if (r->flags & IP_NAT_RANGE_PROTO_RANDOM) + random_selection = 1; + } + + if (mr->info & IPT_SAME_NODST) + printf("nodst "); + + if (random_selection) + printf("random "); +} + +static void SAME_save(const void *ip, const struct xt_entry_target *target) +{ + unsigned int count; + const struct ipt_same_info *mr = (const void *)target->data; + int random_selection = 0; + + for (count = 0; count < mr->rangesize; count++) { + const struct nf_nat_range *r = &mr->range[count]; + struct in_addr a; + + a.s_addr = r->min_ip; + printf("--to %s", xtables_ipaddr_to_numeric(&a)); + a.s_addr = r->max_ip; + + if (r->min_ip == r->max_ip) + printf(" "); + else + printf("-%s ", xtables_ipaddr_to_numeric(&a)); + if (r->flags & IP_NAT_RANGE_PROTO_RANDOM) + random_selection = 1; + } + + if (mr->info & IPT_SAME_NODST) + printf("--nodst "); + + if (random_selection) + printf("--random "); +} + +static struct xtables_target same_tg_reg = { + .name = "SAME", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct ipt_same_info)), + .userspacesize = XT_ALIGN(sizeof(struct ipt_same_info)), + .help = SAME_help, + .init = SAME_init, + .parse = SAME_parse, + .final_check = SAME_check, + .print = SAME_print, + .save = SAME_save, + .extra_opts = SAME_opts, +}; + +void _init(void) +{ + xtables_register_target(&same_tg_reg); +} diff --git a/extensions/libipt_SAME.man b/extensions/libipt_SAME.man new file mode 100644 index 0000000..a99dc73 --- /dev/null +++ b/extensions/libipt_SAME.man @@ -0,0 +1,17 @@ +Similar to SNAT/DNAT depending on chain: it takes a range of addresses +(`\-\-to 1.2.3.4\-1.2.3.7') and gives a client the same +source-/destination-address for each connection. +.PP +N.B.: The DNAT target's \fB\-\-persistent\fP option replaced the SAME target. +.TP +\fB\-\-to\fP \fIipaddr\fP[\fB\-\fP\fIipaddr\fP] +Addresses to map source to. May be specified more than once for +multiple ranges. +.TP +\fB\-\-nodst\fP +Don't use the destination-ip in the calculations when selecting the +new source-ip +.TP +\fB\-\-random\fP +Port mapping will be forcibly randomized to avoid attacks based on +port prediction (kernel >= 2.6.21). diff --git a/extensions/libipt_SNAT.c b/extensions/libipt_SNAT.c new file mode 100644 index 0000000..f7c93d8 --- /dev/null +++ b/extensions/libipt_SNAT.c @@ -0,0 +1,269 @@ +/* Shared library add-on to iptables to add source-NAT support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <iptables.h> +#include <limits.h> /* INT_MAX in ip_tables.h */ +#include <linux/netfilter_ipv4/ip_tables.h> +#include <net/netfilter/nf_nat.h> + +#define IPT_SNAT_OPT_SOURCE 0x01 +#define IPT_SNAT_OPT_RANDOM 0x02 + +/* Source NAT data consists of a multi-range, indicating where to map + to. */ +struct ipt_natinfo +{ + struct xt_entry_target t; + struct nf_nat_multi_range mr; +}; + +static void SNAT_help(void) +{ + printf( +"SNAT target options:\n" +" --to-source <ipaddr>[-<ipaddr>][:port-port]\n" +" Address to map source to.\n" +"[--random] [--persistent]\n"); +} + +static const struct option SNAT_opts[] = { + { "to-source", 1, NULL, '1' }, + { "random", 0, NULL, '2' }, + { "persistent", 0, NULL, '3' }, + { .name = NULL } +}; + +static struct ipt_natinfo * +append_range(struct ipt_natinfo *info, const struct nf_nat_range *range) +{ + unsigned int size; + + /* One rangesize already in struct ipt_natinfo */ + size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range)); + + info = realloc(info, size); + if (!info) + xtables_error(OTHER_PROBLEM, "Out of memory\n"); + + info->t.u.target_size = size; + info->mr.range[info->mr.rangesize] = *range; + info->mr.rangesize++; + + return info; +} + +/* Ranges expected in network order. */ +static struct xt_entry_target * +parse_to(char *arg, int portok, struct ipt_natinfo *info) +{ + struct nf_nat_range range; + char *colon, *dash, *error; + const struct in_addr *ip; + + memset(&range, 0, sizeof(range)); + colon = strchr(arg, ':'); + + if (colon) { + int port; + + if (!portok) + xtables_error(PARAMETER_PROBLEM, + "Need TCP, UDP, SCTP or DCCP with port specification"); + + range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; + + port = atoi(colon+1); + if (port <= 0 || port > 65535) + xtables_error(PARAMETER_PROBLEM, + "Port `%s' not valid\n", colon+1); + + error = strchr(colon+1, ':'); + if (error) + xtables_error(PARAMETER_PROBLEM, + "Invalid port:port syntax - use dash\n"); + + dash = strchr(colon, '-'); + if (!dash) { + range.min.tcp.port + = range.max.tcp.port + = htons(port); + } else { + int maxport; + + maxport = atoi(dash + 1); + if (maxport <= 0 || maxport > 65535) + xtables_error(PARAMETER_PROBLEM, + "Port `%s' not valid\n", dash+1); + if (maxport < port) + /* People are stupid. */ + xtables_error(PARAMETER_PROBLEM, + "Port range `%s' funky\n", colon+1); + range.min.tcp.port = htons(port); + range.max.tcp.port = htons(maxport); + } + /* Starts with a colon? No IP info...*/ + if (colon == arg) + return &(append_range(info, &range)->t); + *colon = '\0'; + } + + range.flags |= IP_NAT_RANGE_MAP_IPS; + dash = strchr(arg, '-'); + if (colon && dash && dash > colon) + dash = NULL; + + if (dash) + *dash = '\0'; + + ip = xtables_numeric_to_ipaddr(arg); + if (!ip) + xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n", + arg); + range.min_ip = ip->s_addr; + if (dash) { + ip = xtables_numeric_to_ipaddr(dash+1); + if (!ip) + xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n", + dash+1); + range.max_ip = ip->s_addr; + } else + range.max_ip = range.min_ip; + + return &(append_range(info, &range)->t); +} + +static int SNAT_parse(int c, char **argv, int invert, unsigned int *flags, + const void *e, struct xt_entry_target **target) +{ + const struct ipt_entry *entry = e; + struct ipt_natinfo *info = (void *)*target; + int portok; + + if (entry->ip.proto == IPPROTO_TCP + || entry->ip.proto == IPPROTO_UDP + || entry->ip.proto == IPPROTO_SCTP + || entry->ip.proto == IPPROTO_DCCP + || entry->ip.proto == IPPROTO_ICMP) + portok = 1; + else + portok = 0; + + switch (c) { + case '1': + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --to-source"); + + if (*flags & IPT_SNAT_OPT_SOURCE) { + if (!kernel_version) + get_kernel_version(); + if (kernel_version > LINUX_VERSION(2, 6, 10)) + xtables_error(PARAMETER_PROBLEM, + "Multiple --to-source not supported"); + } + *target = parse_to(optarg, portok, info); + /* WTF do we need this for?? */ + if (*flags & IPT_SNAT_OPT_RANDOM) + info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; + *flags |= IPT_SNAT_OPT_SOURCE; + return 1; + + case '2': + if (*flags & IPT_SNAT_OPT_SOURCE) { + info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; + *flags |= IPT_SNAT_OPT_RANDOM; + } else + *flags |= IPT_SNAT_OPT_RANDOM; + return 1; + + case '3': + info->mr.range[0].flags |= IP_NAT_RANGE_PERSISTENT; + return 1; + + default: + return 0; + } +} + +static void SNAT_check(unsigned int flags) +{ + if (!(flags & IPT_SNAT_OPT_SOURCE)) + xtables_error(PARAMETER_PROBLEM, + "You must specify --to-source"); +} + +static void print_range(const struct nf_nat_range *r) +{ + if (r->flags & IP_NAT_RANGE_MAP_IPS) { + struct in_addr a; + + a.s_addr = r->min_ip; + printf("%s", xtables_ipaddr_to_numeric(&a)); + if (r->max_ip != r->min_ip) { + a.s_addr = r->max_ip; + printf("-%s", xtables_ipaddr_to_numeric(&a)); + } + } + if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) { + printf(":"); + printf("%hu", ntohs(r->min.tcp.port)); + if (r->max.tcp.port != r->min.tcp.port) + printf("-%hu", ntohs(r->max.tcp.port)); + } +} + +static void SNAT_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct ipt_natinfo *info = (const void *)target; + unsigned int i = 0; + + printf("to:"); + for (i = 0; i < info->mr.rangesize; i++) { + print_range(&info->mr.range[i]); + printf(" "); + if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM) + printf("random "); + if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT) + printf("persistent "); + } +} + +static void SNAT_save(const void *ip, const struct xt_entry_target *target) +{ + const struct ipt_natinfo *info = (const void *)target; + unsigned int i = 0; + + for (i = 0; i < info->mr.rangesize; i++) { + printf("--to-source "); + print_range(&info->mr.range[i]); + printf(" "); + if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM) + printf("--random "); + if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT) + printf("--persistent "); + } +} + +static struct xtables_target snat_tg_reg = { + .name = "SNAT", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct nf_nat_multi_range)), + .userspacesize = XT_ALIGN(sizeof(struct nf_nat_multi_range)), + .help = SNAT_help, + .parse = SNAT_parse, + .final_check = SNAT_check, + .print = SNAT_print, + .save = SNAT_save, + .extra_opts = SNAT_opts, +}; + +void _init(void) +{ + xtables_register_target(&snat_tg_reg); +} diff --git a/extensions/libipt_SNAT.man b/extensions/libipt_SNAT.man new file mode 100644 index 0000000..6b828fd --- /dev/null +++ b/extensions/libipt_SNAT.man @@ -0,0 +1,37 @@ +This target is only valid in the +.B nat +table, in the +.B POSTROUTING +chain. It specifies that the source address of the packet 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\-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 +\fB\-p tcp\fP +or +\fB\-p udp\fP). +If no port range is specified, then source ports below 512 will be +mapped to other ports below 512: those between 512 and 1023 inclusive +will be mapped to ports below 1024, and other ports will be mapped to +1024 or above. Where possible, no port alteration will + +In Kernels up to 2.6.10, you can add several \-\-to\-source options. For those +kernels, if you specify more than one source address, either via an address +range or multiple \-\-to\-source options, a simple round-robin (one after another +in cycle) takes place between these addresses. +Later Kernels (>= 2.6.11-rc1) don't have the ability to NAT to multiple ranges +anymore. +.TP +\fB\-\-random\fP +If option +\fB\-\-random\fP +is used then port mapping will be randomized (kernel >= 2.6.21). +.TP +\fB\-\-persistent\fP +Gives a client the same source-/destination-address for each connection. +This supersedes the SAME target. Support for persistent mappings is available +from 2.6.29-rc2. diff --git a/extensions/libipt_TTL.c b/extensions/libipt_TTL.c new file mode 100644 index 0000000..4db9bbe --- /dev/null +++ b/extensions/libipt_TTL.c @@ -0,0 +1,157 @@ +/* Shared library add-on to iptables for the TTL target + * (C) 2000 by Harald Welte <laforge@gnumonks.org> + * + * $Id$ + * + * This program is distributed under the terms of GNU GPL + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> + +#include <linux/netfilter_ipv4/ipt_TTL.h> + +#define IPT_TTL_USED 1 + +static void TTL_help(void) +{ + printf( +"TTL target options\n" +" --ttl-set value Set TTL to <value 0-255>\n" +" --ttl-dec value Decrement TTL by <value 1-255>\n" +" --ttl-inc value Increment TTL by <value 1-255>\n"); +} + +static int TTL_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct ipt_TTL_info *info = (struct ipt_TTL_info *) (*target)->data; + unsigned int value; + + if (*flags & IPT_TTL_USED) { + xtables_error(PARAMETER_PROBLEM, + "Can't specify TTL option twice"); + } + + if (!optarg) + xtables_error(PARAMETER_PROBLEM, + "TTL: You must specify a value"); + + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "TTL: unexpected `!'"); + + if (!xtables_strtoui(optarg, NULL, &value, 0, UINT8_MAX)) + xtables_error(PARAMETER_PROBLEM, + "TTL: Expected value between 0 and 255"); + + switch (c) { + + case '1': + info->mode = IPT_TTL_SET; + break; + + case '2': + if (value == 0) { + xtables_error(PARAMETER_PROBLEM, + "TTL: decreasing by 0?"); + } + + info->mode = IPT_TTL_DEC; + break; + + case '3': + if (value == 0) { + xtables_error(PARAMETER_PROBLEM, + "TTL: increasing by 0?"); + } + + info->mode = IPT_TTL_INC; + break; + + default: + return 0; + + } + + info->ttl = value; + *flags |= IPT_TTL_USED; + + return 1; +} + +static void TTL_check(unsigned int flags) +{ + if (!(flags & IPT_TTL_USED)) + xtables_error(PARAMETER_PROBLEM, + "TTL: You must specify an action"); +} + +static void TTL_save(const void *ip, const struct xt_entry_target *target) +{ + const struct ipt_TTL_info *info = + (struct ipt_TTL_info *) target->data; + + switch (info->mode) { + case IPT_TTL_SET: + printf("--ttl-set "); + break; + case IPT_TTL_DEC: + printf("--ttl-dec "); + break; + + case IPT_TTL_INC: + printf("--ttl-inc "); + break; + } + printf("%u ", info->ttl); +} + +static void TTL_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct ipt_TTL_info *info = + (struct ipt_TTL_info *) target->data; + + printf("TTL "); + switch (info->mode) { + case IPT_TTL_SET: + printf("set to "); + break; + case IPT_TTL_DEC: + printf("decrement by "); + break; + case IPT_TTL_INC: + printf("increment by "); + break; + } + printf("%u ", info->ttl); +} + +static const struct option TTL_opts[] = { + { "ttl-set", 1, NULL, '1' }, + { "ttl-dec", 1, NULL, '2' }, + { "ttl-inc", 1, NULL, '3' }, + { .name = NULL } +}; + +static struct xtables_target ttl_tg_reg = { + .name = "TTL", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct ipt_TTL_info)), + .userspacesize = XT_ALIGN(sizeof(struct ipt_TTL_info)), + .help = TTL_help, + .parse = TTL_parse, + .final_check = TTL_check, + .print = TTL_print, + .save = TTL_save, + .extra_opts = TTL_opts, +}; + +void _init(void) +{ + xtables_register_target(&ttl_tg_reg); +} diff --git a/extensions/libipt_TTL.man b/extensions/libipt_TTL.man new file mode 100644 index 0000000..89fc18f --- /dev/null +++ b/extensions/libipt_TTL.man @@ -0,0 +1,19 @@ +This is used to modify the IPv4 TTL header field. The TTL field determines +how many hops (routers) a packet can traverse until it's time to live is +exceeded. +.PP +Setting or incrementing the TTL field can potentially be very dangerous, +so it should be avoided at any cost. +.PP +.B Don't ever set or increment the value on packets that leave your local network! +.B mangle +table. +.TP +\fB\-\-ttl\-set\fP \fIvalue\fP +Set the TTL value to `value'. +.TP +\fB\-\-ttl\-dec\fP \fIvalue\fP +Decrement the TTL value `value' times. +.TP +\fB\-\-ttl\-inc\fP \fIvalue\fP +Increment the TTL value `value' times. diff --git a/extensions/libipt_ULOG.c b/extensions/libipt_ULOG.c new file mode 100644 index 0000000..4d009b7 --- /dev/null +++ b/extensions/libipt_ULOG.c @@ -0,0 +1,198 @@ +/* Shared library add-on to iptables to add ULOG support. + * + * (C) 2000 by Harald Welte <laforge@gnumonks.org> + * + * multipart netlink support based on ideas by Sebastian Zander + * <zander@fokus.gmd.de> + * + * This software is released under the terms of GNU GPL + * + * libipt_ULOG.c,v 1.7 2001/01/30 11:55:02 laforge Exp + */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> +#include <getopt.h> +#include <xtables.h> +/* For 64bit kernel / 32bit userspace */ +#include <linux/netfilter_ipv4/ipt_ULOG.h> + + +static void print_groups(unsigned int gmask) +{ + int b; + unsigned int test; + + for (b = 31; b >= 0; b--) { + test = (1 << b); + if (gmask & test) + printf("%d ", b + 1); + } +} + +static void ULOG_help(void) +{ + printf("ULOG target options:\n" + " --ulog-nlgroup nlgroup NETLINK group used for logging\n" + " --ulog-cprange size Bytes of each packet to be passed\n" + " --ulog-qthreshold Threshold of in-kernel queue\n" + " --ulog-prefix prefix Prefix log messages with this prefix.\n"); +} + +static const struct option ULOG_opts[] = { + {"ulog-nlgroup", 1, NULL, '!'}, + {"ulog-prefix", 1, NULL, '#'}, + {"ulog-cprange", 1, NULL, 'A'}, + {"ulog-qthreshold", 1, NULL, 'B'}, + { .name = NULL } +}; + +static void ULOG_init(struct xt_entry_target *t) +{ + struct ipt_ulog_info *loginfo = (struct ipt_ulog_info *) t->data; + + loginfo->nl_group = ULOG_DEFAULT_NLGROUP; + loginfo->qthreshold = ULOG_DEFAULT_QTHRESHOLD; + +} + +#define IPT_LOG_OPT_NLGROUP 0x01 +#define IPT_LOG_OPT_PREFIX 0x02 +#define IPT_LOG_OPT_CPRANGE 0x04 +#define IPT_LOG_OPT_QTHRESHOLD 0x08 + +static int ULOG_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct ipt_ulog_info *loginfo = + (struct ipt_ulog_info *) (*target)->data; + int group_d; + + switch (c) { + case '!': + if (*flags & IPT_LOG_OPT_NLGROUP) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --ulog-nlgroup twice"); + + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --ulog-nlgroup"); + group_d = atoi(optarg); + if (group_d > 32 || group_d < 1) + xtables_error(PARAMETER_PROBLEM, + "--ulog-nlgroup has to be between 1 and 32"); + + loginfo->nl_group = (1 << (group_d - 1)); + + *flags |= IPT_LOG_OPT_NLGROUP; + break; + + case '#': + if (*flags & IPT_LOG_OPT_PREFIX) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --ulog-prefix twice"); + + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --ulog-prefix"); + + if (strlen(optarg) > sizeof(loginfo->prefix) - 1) + xtables_error(PARAMETER_PROBLEM, + "Maximum prefix length %u for --ulog-prefix", + (unsigned int)sizeof(loginfo->prefix) - 1); + + if (strlen(optarg) == 0) + xtables_error(PARAMETER_PROBLEM, + "No prefix specified for --ulog-prefix"); + + if (strlen(optarg) != strlen(strtok(optarg, "\n"))) + xtables_error(PARAMETER_PROBLEM, + "Newlines not allowed in --ulog-prefix"); + + strcpy(loginfo->prefix, optarg); + *flags |= IPT_LOG_OPT_PREFIX; + break; + case 'A': + if (*flags & IPT_LOG_OPT_CPRANGE) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --ulog-cprange twice"); + if (atoi(optarg) < 0) + xtables_error(PARAMETER_PROBLEM, + "Negative copy range?"); + loginfo->copy_range = atoi(optarg); + *flags |= IPT_LOG_OPT_CPRANGE; + break; + case 'B': + if (*flags & IPT_LOG_OPT_QTHRESHOLD) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --ulog-qthreshold twice"); + if (atoi(optarg) < 1) + xtables_error(PARAMETER_PROBLEM, + "Negative or zero queue threshold ?"); + if (atoi(optarg) > ULOG_MAX_QLEN) + xtables_error(PARAMETER_PROBLEM, + "Maximum queue length exceeded"); + loginfo->qthreshold = atoi(optarg); + *flags |= IPT_LOG_OPT_QTHRESHOLD; + break; + default: + return 0; + } + return 1; +} + +static void ULOG_save(const void *ip, const struct xt_entry_target *target) +{ + const struct ipt_ulog_info *loginfo + = (const struct ipt_ulog_info *) target->data; + + if (strcmp(loginfo->prefix, "") != 0) { + fputs("--ulog-prefix ", stdout); + xtables_save_string(loginfo->prefix); + } + + if (loginfo->nl_group != ULOG_DEFAULT_NLGROUP) { + printf("--ulog-nlgroup "); + print_groups(loginfo->nl_group); + } + if (loginfo->copy_range) + printf("--ulog-cprange %u ", (unsigned int)loginfo->copy_range); + + if (loginfo->qthreshold != ULOG_DEFAULT_QTHRESHOLD) + printf("--ulog-qthreshold %u ", (unsigned int)loginfo->qthreshold); +} + +static void ULOG_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct ipt_ulog_info *loginfo + = (const struct ipt_ulog_info *) target->data; + + printf("ULOG "); + printf("copy_range %u nlgroup ", (unsigned int)loginfo->copy_range); + print_groups(loginfo->nl_group); + if (strcmp(loginfo->prefix, "") != 0) + printf("prefix `%s' ", loginfo->prefix); + printf("queue_threshold %u ", (unsigned int)loginfo->qthreshold); +} + +static struct xtables_target ulog_tg_reg = { + .name = "ULOG", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct ipt_ulog_info)), + .userspacesize = XT_ALIGN(sizeof(struct ipt_ulog_info)), + .help = ULOG_help, + .init = ULOG_init, + .parse = ULOG_parse, + .print = ULOG_print, + .save = ULOG_save, + .extra_opts = ULOG_opts, +}; + +void _init(void) +{ + xtables_register_target(&ulog_tg_reg); +} diff --git a/extensions/libipt_ULOG.man b/extensions/libipt_ULOG.man new file mode 100644 index 0000000..649b6e3 --- /dev/null +++ b/extensions/libipt_ULOG.man @@ -0,0 +1,27 @@ +This target provides userspace logging of matching packets. When this +target is set for a rule, the Linux kernel will multicast this packet +through a +.IR netlink +socket. One or more userspace processes may then subscribe to various +multicast groups and receive the packets. +Like LOG, this is a "non-terminating target", i.e. rule traversal +continues at the next rule. +.TP +\fB\-\-ulog\-nlgroup\fP \fInlgroup\fP +This specifies the netlink group (1-32) to which the packet is sent. +Default value is 1. +.TP +\fB\-\-ulog\-prefix\fP \fIprefix\fP +Prefix log messages with the specified prefix; up to 32 characters +long, and useful for distinguishing messages in the logs. +.TP +\fB\-\-ulog\-cprange\fP \fIsize\fP +Number of bytes to be copied to userspace. A value of 0 always copies +the entire packet, regardless of its size. Default is 0. +.TP +\fB\-\-ulog\-qthreshold\fP \fIsize\fP +Number of packet to queue inside kernel. Setting this value to, e.g. 10 +accumulates ten packets inside the kernel and transmits them as one +netlink multipart message to userspace. Default is 1 (for backwards +compatibility). +.br diff --git a/extensions/libipt_addrtype.c b/extensions/libipt_addrtype.c new file mode 100644 index 0000000..ad63dcf --- /dev/null +++ b/extensions/libipt_addrtype.c @@ -0,0 +1,360 @@ +/* Shared library add-on to iptables to add addrtype matching support + * + * This program is released under the terms of GNU GPL */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <xtables.h> + +#include <linux/netfilter_ipv4/ipt_addrtype.h> + +/* from linux/rtnetlink.h, must match order of enumeration */ +static const char *const rtn_names[] = { + "UNSPEC", + "UNICAST", + "LOCAL", + "BROADCAST", + "ANYCAST", + "MULTICAST", + "BLACKHOLE", + "UNREACHABLE", + "PROHIBIT", + "THROW", + "NAT", + "XRESOLVE", + NULL +}; + +static void addrtype_help_types(void) +{ + int i; + + for (i = 0; rtn_names[i]; i++) + printf(" %s\n", rtn_names[i]); +} + +static void addrtype_help_v0(void) +{ + printf( +"Address type match options:\n" +" [!] --src-type type[,...] Match source address type\n" +" [!] --dst-type type[,...] Match destination address type\n" +"\n" +"Valid types: \n"); + addrtype_help_types(); +} + +static void addrtype_help_v1(void) +{ + printf( +"Address type match options:\n" +" [!] --src-type type[,...] Match source address type\n" +" [!] --dst-type type[,...] Match destination address type\n" +" --limit-iface-in Match only on the packet's incoming device\n" +" --limit-iface-out Match only on the packet's incoming device\n" +"\n" +"Valid types: \n"); + addrtype_help_types(); +} + +static int +parse_type(const char *name, size_t len, u_int16_t *mask) +{ + int i; + + for (i = 0; rtn_names[i]; i++) + if (strncasecmp(name, rtn_names[i], len) == 0) { + /* build up bitmask for kernel module */ + *mask |= (1 << i); + return 1; + } + + return 0; +} + +static void parse_types(const char *arg, u_int16_t *mask) +{ + const char *comma; + + while ((comma = strchr(arg, ',')) != NULL) { + if (comma == arg || !parse_type(arg, comma-arg, mask)) + xtables_error(PARAMETER_PROBLEM, + "addrtype: bad type `%s'", arg); + arg = comma + 1; + } + + if (strlen(arg) == 0 || !parse_type(arg, strlen(arg), mask)) + xtables_error(PARAMETER_PROBLEM, "addrtype: bad type \"%s\"", arg); +} + +#define IPT_ADDRTYPE_OPT_SRCTYPE 0x1 +#define IPT_ADDRTYPE_OPT_DSTTYPE 0x2 +#define IPT_ADDRTYPE_OPT_LIMIT_IFACE_IN 0x4 +#define IPT_ADDRTYPE_OPT_LIMIT_IFACE_OUT 0x8 + +static int +addrtype_parse_v0(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ipt_addrtype_info *info = + (struct ipt_addrtype_info *) (*match)->data; + + switch (c) { + case '1': + if (*flags&IPT_ADDRTYPE_OPT_SRCTYPE) + xtables_error(PARAMETER_PROBLEM, + "addrtype: can't specify src-type twice"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_types(optarg, &info->source); + if (invert) + info->invert_source = 1; + *flags |= IPT_ADDRTYPE_OPT_SRCTYPE; + break; + case '2': + if (*flags&IPT_ADDRTYPE_OPT_DSTTYPE) + xtables_error(PARAMETER_PROBLEM, + "addrtype: can't specify dst-type twice"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_types(optarg, &info->dest); + if (invert) + info->invert_dest = 1; + *flags |= IPT_ADDRTYPE_OPT_DSTTYPE; + break; + default: + return 0; + } + + return 1; +} + +static int +addrtype_parse_v1(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ipt_addrtype_info_v1 *info = + (struct ipt_addrtype_info_v1 *) (*match)->data; + + switch (c) { + case '1': + if (*flags & IPT_ADDRTYPE_OPT_SRCTYPE) + xtables_error(PARAMETER_PROBLEM, + "addrtype: can't specify src-type twice"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_types(optarg, &info->source); + if (invert) + info->flags |= IPT_ADDRTYPE_INVERT_SOURCE; + *flags |= IPT_ADDRTYPE_OPT_SRCTYPE; + break; + case '2': + if (*flags & IPT_ADDRTYPE_OPT_DSTTYPE) + xtables_error(PARAMETER_PROBLEM, + "addrtype: can't specify dst-type twice"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_types(optarg, &info->dest); + if (invert) + info->flags |= IPT_ADDRTYPE_INVERT_DEST; + *flags |= IPT_ADDRTYPE_OPT_DSTTYPE; + break; + case '3': + if (*flags & IPT_ADDRTYPE_OPT_LIMIT_IFACE_IN) + xtables_error(PARAMETER_PROBLEM, + "addrtype: can't specify limit-iface-in twice"); + info->flags |= IPT_ADDRTYPE_LIMIT_IFACE_IN; + *flags |= IPT_ADDRTYPE_OPT_LIMIT_IFACE_IN; + break; + case '4': + if (*flags & IPT_ADDRTYPE_OPT_LIMIT_IFACE_OUT) + xtables_error(PARAMETER_PROBLEM, + "addrtype: can't specify limit-iface-out twice"); + info->flags |= IPT_ADDRTYPE_LIMIT_IFACE_OUT; + *flags |= IPT_ADDRTYPE_OPT_LIMIT_IFACE_OUT; + break; + default: + return 0; + } + + return 1; +} + +static void addrtype_check_v0(unsigned int flags) +{ + if (!(flags & (IPT_ADDRTYPE_OPT_SRCTYPE|IPT_ADDRTYPE_OPT_DSTTYPE))) + xtables_error(PARAMETER_PROBLEM, + "addrtype: you must specify --src-type or --dst-type"); +} + +static void addrtype_check_v1(unsigned int flags) +{ + if (!(flags & (IPT_ADDRTYPE_OPT_SRCTYPE|IPT_ADDRTYPE_OPT_DSTTYPE))) + xtables_error(PARAMETER_PROBLEM, + "addrtype: you must specify --src-type or --dst-type"); + if (flags & IPT_ADDRTYPE_OPT_LIMIT_IFACE_IN && + flags & IPT_ADDRTYPE_OPT_LIMIT_IFACE_OUT) + xtables_error(PARAMETER_PROBLEM, + "addrtype: you can't specify both --limit-iface-in " + "and --limit-iface-out"); +} + +static void print_types(u_int16_t mask) +{ + const char *sep = ""; + int i; + + for (i = 0; rtn_names[i]; i++) + if (mask & (1 << i)) { + printf("%s%s", sep, rtn_names[i]); + sep = ","; + } + + printf(" "); +} + +static void addrtype_print_v0(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ipt_addrtype_info *info = + (struct ipt_addrtype_info *) match->data; + + printf("ADDRTYPE match "); + if (info->source) { + printf("src-type "); + if (info->invert_source) + printf("!"); + print_types(info->source); + } + if (info->dest) { + printf("dst-type "); + if (info->invert_dest) + printf("!"); + print_types(info->dest); + } +} + +static void addrtype_print_v1(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ipt_addrtype_info_v1 *info = + (struct ipt_addrtype_info_v1 *) match->data; + + printf("ADDRTYPE match "); + if (info->source) { + printf("src-type "); + if (info->flags & IPT_ADDRTYPE_INVERT_SOURCE) + printf("!"); + print_types(info->source); + } + if (info->dest) { + printf("dst-type "); + if (info->flags & IPT_ADDRTYPE_INVERT_DEST) + printf("!"); + print_types(info->dest); + } + if (info->flags & IPT_ADDRTYPE_LIMIT_IFACE_IN) { + printf("limit-in "); + } + if (info->flags & IPT_ADDRTYPE_LIMIT_IFACE_OUT) { + printf("limit-out "); + } +} + +static void addrtype_save_v0(const void *ip, const struct xt_entry_match *match) +{ + const struct ipt_addrtype_info *info = + (struct ipt_addrtype_info *) match->data; + + if (info->source) { + if (info->invert_source) + printf("! "); + printf("--src-type "); + print_types(info->source); + } + if (info->dest) { + if (info->invert_dest) + printf("! "); + printf("--dst-type "); + print_types(info->dest); + } +} + +static void addrtype_save_v1(const void *ip, const struct xt_entry_match *match) +{ + const struct ipt_addrtype_info_v1 *info = + (struct ipt_addrtype_info_v1 *) match->data; + + if (info->source) { + if (info->flags & IPT_ADDRTYPE_INVERT_SOURCE) + printf("! "); + printf("--src-type "); + print_types(info->source); + } + if (info->dest) { + if (info->flags & IPT_ADDRTYPE_INVERT_DEST) + printf("! "); + printf("--dst-type "); + print_types(info->dest); + } + if (info->flags & IPT_ADDRTYPE_LIMIT_IFACE_IN) { + printf("--limit-iface-in "); + } + if (info->flags & IPT_ADDRTYPE_LIMIT_IFACE_OUT) { + printf("--limit-iface-out "); + } +} + +static const struct option addrtype_opts[] = { + { "src-type", 1, NULL, '1' }, + { "dst-type", 1, NULL, '2' }, + { .name = NULL } +}; + +static const struct option addrtype_opts_v0[] = { + { "src-type", 1, NULL, '1' }, + { "dst-type", 1, NULL, '2' }, + { .name = NULL } +}; + +static const struct option addrtype_opts_v1[] = { + { "src-type", 1, NULL, '1' }, + { "dst-type", 1, NULL, '2' }, + { "limit-iface-in", 0, NULL, '3' }, + { "limit-iface-out", 0, NULL, '4' }, + { .name = NULL } +}; + +static struct xtables_match addrtype_mt_reg[] = { + { + .name = "addrtype", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct ipt_addrtype_info)), + .userspacesize = XT_ALIGN(sizeof(struct ipt_addrtype_info)), + .help = addrtype_help_v0, + .parse = addrtype_parse_v0, + .final_check = addrtype_check_v0, + .print = addrtype_print_v0, + .save = addrtype_save_v0, + .extra_opts = addrtype_opts_v0, + }, + { + .name = "addrtype", + .revision = 1, + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct ipt_addrtype_info_v1)), + .userspacesize = XT_ALIGN(sizeof(struct ipt_addrtype_info_v1)), + .help = addrtype_help_v1, + .parse = addrtype_parse_v1, + .final_check = addrtype_check_v1, + .print = addrtype_print_v1, + .save = addrtype_save_v1, + .extra_opts = addrtype_opts_v1, + }, +}; + + +void _init(void) +{ + xtables_register_matches(addrtype_mt_reg, ARRAY_SIZE(addrtype_mt_reg)); +} diff --git a/extensions/libipt_addrtype.man b/extensions/libipt_addrtype.man new file mode 100644 index 0000000..16fd9df --- /dev/null +++ b/extensions/libipt_addrtype.man @@ -0,0 +1,69 @@ +This module matches packets based on their +.B address type. +Address types are used within the kernel networking stack and categorize +addresses into various groups. The exact definition of that group depends on the specific layer three protocol. +.PP +The following address types are possible: +.TP +.BI "UNSPEC" +an unspecified address (i.e. 0.0.0.0) +.TP +.BI "UNICAST" +an unicast address +.TP +.BI "LOCAL" +a local address +.TP +.BI "BROADCAST" +a broadcast address +.TP +.BI "ANYCAST" +an anycast packet +.TP +.BI "MULTICAST" +a multicast address +.TP +.BI "BLACKHOLE" +a blackhole address +.TP +.BI "UNREACHABLE" +an unreachable address +.TP +.BI "PROHIBIT" +a prohibited address +.TP +.BI "THROW" +FIXME +.TP +.BI "NAT" +FIXME +.TP +.BI "XRESOLVE" +.TP +[\fB!\fP] \fB\-\-src\-type\fP \fItype\fP +Matches if the source address is of given type +.TP +[\fB!\fP] \fB\-\-dst\-type\fP \fItype\fP +Matches if the destination address is of given type +.TP +.BI "\-\-limit\-iface\-in" +The address type checking can be limited to the interface the packet is coming +in. This option is only valid in the +.BR PREROUTING , +.B INPUT +and +.B FORWARD +chains. It cannot be specified with the +\fB\-\-limit\-iface\-out\fP +option. +.TP +\fB\-\-limit\-iface\-out\fP +The address type checking can be limited to the interface the packet is going +out. This option is only valid in the +.BR POSTROUTING , +.B OUTPUT +and +.B FORWARD +chains. It cannot be specified with the +\fB\-\-limit\-iface\-in\fP +option. diff --git a/extensions/libipt_ah.c b/extensions/libipt_ah.c new file mode 100644 index 0000000..170cd8b --- /dev/null +++ b/extensions/libipt_ah.c @@ -0,0 +1,170 @@ +/* Shared library add-on to iptables to add AH support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <errno.h> +#include <xtables.h> +#include <linux/netfilter_ipv4/ipt_ah.h> + +static void ah_help(void) +{ + printf( +"ah match options:\n" +"[!] --ahspi spi[:spi]\n" +" match spi (range)\n"); +} + +static const struct option ah_opts[] = { + { "ahspi", 1, NULL, '1' }, + { .name = NULL } +}; + +static u_int32_t +parse_ah_spi(const char *spistr) +{ + unsigned long int spi; + char* ep; + + spi = strtoul(spistr,&ep,0) ; + + if ( spistr == ep ) { + xtables_error(PARAMETER_PROBLEM, + "AH no valid digits in spi `%s'", spistr); + } + if ( spi == ULONG_MAX && errno == ERANGE ) { + xtables_error(PARAMETER_PROBLEM, + "spi `%s' specified too big: would overflow", spistr); + } + if ( *spistr != '\0' && *ep != '\0' ) { + xtables_error(PARAMETER_PROBLEM, + "AH error parsing spi `%s'", spistr); + } + return spi; +} + +static void +parse_ah_spis(const char *spistring, u_int32_t *spis) +{ + char *buffer; + char *cp; + + buffer = strdup(spistring); + if ((cp = strchr(buffer, ':')) == NULL) + spis[0] = spis[1] = parse_ah_spi(buffer); + else { + *cp = '\0'; + cp++; + + spis[0] = buffer[0] ? parse_ah_spi(buffer) : 0; + spis[1] = cp[0] ? parse_ah_spi(cp) : 0xFFFFFFFF; + } + free(buffer); +} + +static void ah_init(struct xt_entry_match *m) +{ + struct ipt_ah *ahinfo = (struct ipt_ah *)m->data; + + ahinfo->spis[1] = 0xFFFFFFFF; +} + +#define AH_SPI 0x01 + +static int ah_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ipt_ah *ahinfo = (struct ipt_ah *)(*match)->data; + + switch (c) { + case '1': + if (*flags & AH_SPI) + xtables_error(PARAMETER_PROBLEM, + "Only one `--ahspi' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_ah_spis(optarg, ahinfo->spis); + if (invert) + ahinfo->invflags |= IPT_AH_INV_SPI; + *flags |= AH_SPI; + break; + default: + return 0; + } + + return 1; +} + +static void +print_spis(const char *name, u_int32_t min, u_int32_t max, + int invert) +{ + const char *inv = invert ? "!" : ""; + + if (min != 0 || max != 0xFFFFFFFF || invert) { + printf("%s", name); + if (min == max) { + printf(":%s", inv); + printf("%u", min); + } else { + printf("s:%s", inv); + printf("%u",min); + printf(":"); + printf("%u",max); + } + printf(" "); + } +} + +static void ah_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ipt_ah *ah = (struct ipt_ah *)match->data; + + printf("ah "); + print_spis("spi", ah->spis[0], ah->spis[1], + ah->invflags & IPT_AH_INV_SPI); + if (ah->invflags & ~IPT_AH_INV_MASK) + printf("Unknown invflags: 0x%X ", + ah->invflags & ~IPT_AH_INV_MASK); +} + +static void ah_save(const void *ip, const struct xt_entry_match *match) +{ + const struct ipt_ah *ahinfo = (struct ipt_ah *)match->data; + + if (!(ahinfo->spis[0] == 0 + && ahinfo->spis[1] == 0xFFFFFFFF)) { + printf("%s--ahspi ", + (ahinfo->invflags & IPT_AH_INV_SPI) ? "! " : ""); + if (ahinfo->spis[0] + != ahinfo->spis[1]) + printf("%u:%u ", + ahinfo->spis[0], + ahinfo->spis[1]); + else + printf("%u ", + ahinfo->spis[0]); + } + +} + +static struct xtables_match ah_mt_reg = { + .name = "ah", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct ipt_ah)), + .userspacesize = XT_ALIGN(sizeof(struct ipt_ah)), + .help = ah_help, + .init = ah_init, + .parse = ah_parse, + .print = ah_print, + .save = ah_save, + .extra_opts = ah_opts, +}; + +void +_init(void) +{ + xtables_register_match(&ah_mt_reg); +} diff --git a/extensions/libipt_ah.man b/extensions/libipt_ah.man new file mode 100644 index 0000000..d26455e --- /dev/null +++ b/extensions/libipt_ah.man @@ -0,0 +1,3 @@ +This module matches the SPIs in Authentication header of IPsec packets. +.TP +[\fB!\fP] \fB\-\-ahspi\fP \fIspi\fP[\fB:\fP\fIspi\fP] diff --git a/extensions/libipt_ecn.c b/extensions/libipt_ecn.c new file mode 100644 index 0000000..ec3ff2d --- /dev/null +++ b/extensions/libipt_ecn.c @@ -0,0 +1,160 @@ +/* Shared library add-on to iptables for ECN matching + * + * (C) 2002 by Harald Welte <laforge@gnumonks.org> + * + * This program is distributed under the terms of GNU GPL v2, 1991 + * + * libipt_ecn.c borrowed heavily from libipt_dscp.c + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter_ipv4/ipt_ecn.h> + +static void ecn_help(void) +{ + printf( +"ECN match options\n" +"[!] --ecn-tcp-cwr Match CWR bit of TCP header\n" +"[!] --ecn-tcp-ece Match ECE bit of TCP header\n" +"[!] --ecn-ip-ect [0..3] Match ECN codepoint in IPv4 header\n"); +} + +static const struct option ecn_opts[] = { + { .name = "ecn-tcp-cwr", .has_arg = 0, .val = 'F' }, + { .name = "ecn-tcp-ece", .has_arg = 0, .val = 'G' }, + { .name = "ecn-ip-ect", .has_arg = 1, .val = 'H' }, + { .name = NULL } +}; + +static int ecn_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + unsigned int result; + struct ipt_ecn_info *einfo + = (struct ipt_ecn_info *)(*match)->data; + + switch (c) { + case 'F': + if (*flags & IPT_ECN_OP_MATCH_CWR) + xtables_error(PARAMETER_PROBLEM, + "ECN match: can only use parameter ONCE!"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + einfo->operation |= IPT_ECN_OP_MATCH_CWR; + if (invert) + einfo->invert |= IPT_ECN_OP_MATCH_CWR; + *flags |= IPT_ECN_OP_MATCH_CWR; + break; + + case 'G': + if (*flags & IPT_ECN_OP_MATCH_ECE) + xtables_error(PARAMETER_PROBLEM, + "ECN match: can only use parameter ONCE!"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + einfo->operation |= IPT_ECN_OP_MATCH_ECE; + if (invert) + einfo->invert |= IPT_ECN_OP_MATCH_ECE; + *flags |= IPT_ECN_OP_MATCH_ECE; + break; + + case 'H': + if (*flags & IPT_ECN_OP_MATCH_IP) + xtables_error(PARAMETER_PROBLEM, + "ECN match: can only use parameter ONCE!"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) + einfo->invert |= IPT_ECN_OP_MATCH_IP; + *flags |= IPT_ECN_OP_MATCH_IP; + einfo->operation |= IPT_ECN_OP_MATCH_IP; + if (!xtables_strtoui(optarg, NULL, &result, 0, 3)) + xtables_error(PARAMETER_PROBLEM, + "ECN match: Value out of range"); + einfo->ip_ect = result; + break; + default: + return 0; + } + + return 1; +} + +static void ecn_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "ECN match: some option required"); +} + +static void ecn_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ipt_ecn_info *einfo = + (const struct ipt_ecn_info *)match->data; + + printf("ECN match "); + + if (einfo->operation & IPT_ECN_OP_MATCH_ECE) { + if (einfo->invert & IPT_ECN_OP_MATCH_ECE) + fputc('!', stdout); + printf("ECE "); + } + + if (einfo->operation & IPT_ECN_OP_MATCH_CWR) { + if (einfo->invert & IPT_ECN_OP_MATCH_CWR) + fputc('!', stdout); + printf("CWR "); + } + + if (einfo->operation & IPT_ECN_OP_MATCH_IP) { + if (einfo->invert & IPT_ECN_OP_MATCH_IP) + fputc('!', stdout); + printf("ECT=%d ", einfo->ip_ect); + } +} + +static void ecn_save(const void *ip, const struct xt_entry_match *match) +{ + const struct ipt_ecn_info *einfo = + (const struct ipt_ecn_info *)match->data; + + if (einfo->operation & IPT_ECN_OP_MATCH_ECE) { + if (einfo->invert & IPT_ECN_OP_MATCH_ECE) + printf("! "); + printf("--ecn-tcp-ece "); + } + + if (einfo->operation & IPT_ECN_OP_MATCH_CWR) { + if (einfo->invert & IPT_ECN_OP_MATCH_CWR) + printf("! "); + printf("--ecn-tcp-cwr "); + } + + if (einfo->operation & IPT_ECN_OP_MATCH_IP) { + if (einfo->invert & IPT_ECN_OP_MATCH_IP) + printf("! "); + printf("--ecn-ip-ect %d", einfo->ip_ect); + } +} + +static struct xtables_match ecn_mt_reg = { + .name = "ecn", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct ipt_ecn_info)), + .userspacesize = XT_ALIGN(sizeof(struct ipt_ecn_info)), + .help = ecn_help, + .parse = ecn_parse, + .final_check = ecn_check, + .print = ecn_print, + .save = ecn_save, + .extra_opts = ecn_opts, +}; + +void _init(void) +{ + xtables_register_match(&ecn_mt_reg); +} diff --git a/extensions/libipt_ecn.man b/extensions/libipt_ecn.man new file mode 100644 index 0000000..7f80647 --- /dev/null +++ b/extensions/libipt_ecn.man @@ -0,0 +1,11 @@ +This allows you to match the ECN bits of the IPv4 and TCP header. ECN is the Explicit Congestion Notification mechanism as specified in RFC3168 +.TP +[\fB!\fP] \fB\-\-ecn\-tcp\-cwr\fP +This matches if the TCP ECN CWR (Congestion Window Received) bit is set. +.TP +[\fB!\fP] \fB\-\-ecn\-tcp\-ece\fP +This matches if the TCP ECN ECE (ECN Echo) bit is set. +.TP +[\fB!\fP] \fB\-\-ecn\-ip\-ect\fP \fInum\fP +This matches a particular IPv4 ECT (ECN-Capable Transport). You have to specify +a number between `0' and `3'. diff --git a/extensions/libipt_icmp.c b/extensions/libipt_icmp.c new file mode 100644 index 0000000..37b2fdc --- /dev/null +++ b/extensions/libipt_icmp.c @@ -0,0 +1,285 @@ +/* Shared library add-on to iptables to add ICMP support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <limits.h> /* INT_MAX in ip_tables.h */ +#include <linux/netfilter_ipv4/ip_tables.h> + +/* special hack for icmp-type 'any': + * Up to kernel <=2.4.20 the problem was: + * '-p icmp ' matches all icmp packets + * '-p icmp -m icmp' matches _only_ ICMP type 0 :( + * This is now fixed by initializing the field * to icmp type 0xFF + * See: https://bugzilla.netfilter.org/cgi-bin/bugzilla/show_bug.cgi?id=37 + */ + +struct icmp_names { + const char *name; + u_int8_t type; + u_int8_t code_min, code_max; +}; + +static const struct icmp_names icmp_codes[] = { + { "any", 0xFF, 0, 0xFF }, + { "echo-reply", 0, 0, 0xFF }, + /* Alias */ { "pong", 0, 0, 0xFF }, + + { "destination-unreachable", 3, 0, 0xFF }, + { "network-unreachable", 3, 0, 0 }, + { "host-unreachable", 3, 1, 1 }, + { "protocol-unreachable", 3, 2, 2 }, + { "port-unreachable", 3, 3, 3 }, + { "fragmentation-needed", 3, 4, 4 }, + { "source-route-failed", 3, 5, 5 }, + { "network-unknown", 3, 6, 6 }, + { "host-unknown", 3, 7, 7 }, + { "network-prohibited", 3, 9, 9 }, + { "host-prohibited", 3, 10, 10 }, + { "TOS-network-unreachable", 3, 11, 11 }, + { "TOS-host-unreachable", 3, 12, 12 }, + { "communication-prohibited", 3, 13, 13 }, + { "host-precedence-violation", 3, 14, 14 }, + { "precedence-cutoff", 3, 15, 15 }, + + { "source-quench", 4, 0, 0xFF }, + + { "redirect", 5, 0, 0xFF }, + { "network-redirect", 5, 0, 0 }, + { "host-redirect", 5, 1, 1 }, + { "TOS-network-redirect", 5, 2, 2 }, + { "TOS-host-redirect", 5, 3, 3 }, + + { "echo-request", 8, 0, 0xFF }, + /* Alias */ { "ping", 8, 0, 0xFF }, + + { "router-advertisement", 9, 0, 0xFF }, + + { "router-solicitation", 10, 0, 0xFF }, + + { "time-exceeded", 11, 0, 0xFF }, + /* Alias */ { "ttl-exceeded", 11, 0, 0xFF }, + { "ttl-zero-during-transit", 11, 0, 0 }, + { "ttl-zero-during-reassembly", 11, 1, 1 }, + + { "parameter-problem", 12, 0, 0xFF }, + { "ip-header-bad", 12, 0, 0 }, + { "required-option-missing", 12, 1, 1 }, + + { "timestamp-request", 13, 0, 0xFF }, + + { "timestamp-reply", 14, 0, 0xFF }, + + { "address-mask-request", 17, 0, 0xFF }, + + { "address-mask-reply", 18, 0, 0xFF } +}; + +static void +print_icmptypes(void) +{ + unsigned int i; + printf("Valid ICMP Types:"); + + for (i = 0; i < ARRAY_SIZE(icmp_codes); ++i) { + if (i && icmp_codes[i].type == icmp_codes[i-1].type) { + if (icmp_codes[i].code_min == icmp_codes[i-1].code_min + && (icmp_codes[i].code_max + == icmp_codes[i-1].code_max)) + printf(" (%s)", icmp_codes[i].name); + else + printf("\n %s", icmp_codes[i].name); + } + else + printf("\n%s", icmp_codes[i].name); + } + printf("\n"); +} + +static void icmp_help(void) +{ + printf( +"icmp match options:\n" +"[!] --icmp-type typename match icmp type\n" +"[!] --icmp-type type[/code] (or numeric type or type/code)\n"); + print_icmptypes(); +} + +static const struct option icmp_opts[] = { + { "icmp-type", 1, NULL, '1' }, + { .name = NULL } +}; + +static void +parse_icmp(const char *icmptype, u_int8_t *type, u_int8_t code[]) +{ + static const unsigned int limit = ARRAY_SIZE(icmp_codes); + unsigned int match = limit; + unsigned int i; + + for (i = 0; i < limit; i++) { + if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype)) + == 0) { + if (match != limit) + xtables_error(PARAMETER_PROBLEM, + "Ambiguous ICMP type `%s':" + " `%s' or `%s'?", + icmptype, + icmp_codes[match].name, + icmp_codes[i].name); + match = i; + } + } + + if (match != limit) { + *type = icmp_codes[match].type; + code[0] = icmp_codes[match].code_min; + code[1] = icmp_codes[match].code_max; + } else { + char *slash; + char buffer[strlen(icmptype) + 1]; + unsigned int number; + + strcpy(buffer, icmptype); + slash = strchr(buffer, '/'); + + if (slash) + *slash = '\0'; + + if (!xtables_strtoui(buffer, NULL, &number, 0, UINT8_MAX)) + xtables_error(PARAMETER_PROBLEM, + "Invalid ICMP type `%s'\n", buffer); + *type = number; + if (slash) { + if (!xtables_strtoui(slash+1, NULL, &number, 0, UINT8_MAX)) + xtables_error(PARAMETER_PROBLEM, + "Invalid ICMP code `%s'\n", + slash+1); + code[0] = code[1] = number; + } else { + code[0] = 0; + code[1] = 0xFF; + } + } +} + +static void icmp_init(struct xt_entry_match *m) +{ + struct ipt_icmp *icmpinfo = (struct ipt_icmp *)m->data; + + icmpinfo->type = 0xFF; + icmpinfo->code[1] = 0xFF; +} + +static int icmp_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ipt_icmp *icmpinfo = (struct ipt_icmp *)(*match)->data; + + switch (c) { + case '1': + if (*flags == 1) + xtables_error(PARAMETER_PROBLEM, + "icmp match: only use --icmp-type once!"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_icmp(optarg, &icmpinfo->type, + icmpinfo->code); + if (invert) + icmpinfo->invflags |= IPT_ICMP_INV; + *flags = 1; + break; + + default: + return 0; + } + + return 1; +} + +static void print_icmptype(u_int8_t type, + u_int8_t code_min, u_int8_t code_max, + int invert, + int numeric) +{ + if (!numeric) { + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(icmp_codes); ++i) + if (icmp_codes[i].type == type + && icmp_codes[i].code_min == code_min + && icmp_codes[i].code_max == code_max) + break; + + if (i != ARRAY_SIZE(icmp_codes)) { + printf("%s%s ", + invert ? "!" : "", + icmp_codes[i].name); + return; + } + } + + if (invert) + printf("!"); + + printf("type %u", type); + if (code_min == 0 && code_max == 0xFF) + printf(" "); + else if (code_min == code_max) + printf(" code %u ", code_min); + else + printf(" codes %u-%u ", code_min, code_max); +} + +static void icmp_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data; + + printf("icmp "); + print_icmptype(icmp->type, icmp->code[0], icmp->code[1], + icmp->invflags & IPT_ICMP_INV, + numeric); + + if (icmp->invflags & ~IPT_ICMP_INV) + printf("Unknown invflags: 0x%X ", + icmp->invflags & ~IPT_ICMP_INV); +} + +static void icmp_save(const void *ip, const struct xt_entry_match *match) +{ + const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data; + + if (icmp->invflags & IPT_ICMP_INV) + printf("! "); + + /* special hack for 'any' case */ + if (icmp->type == 0xFF) { + printf("--icmp-type any "); + } else { + printf("--icmp-type %u", icmp->type); + if (icmp->code[0] != 0 || icmp->code[1] != 0xFF) + printf("/%u", icmp->code[0]); + printf(" "); + } +} + +static struct xtables_match icmp_mt_reg = { + .name = "icmp", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct ipt_icmp)), + .userspacesize = XT_ALIGN(sizeof(struct ipt_icmp)), + .help = icmp_help, + .init = icmp_init, + .parse = icmp_parse, + .print = icmp_print, + .save = icmp_save, + .extra_opts = icmp_opts, +}; + +void _init(void) +{ + xtables_register_match(&icmp_mt_reg); +} diff --git a/extensions/libipt_icmp.man b/extensions/libipt_icmp.man new file mode 100644 index 0000000..1039704 --- /dev/null +++ b/extensions/libipt_icmp.man @@ -0,0 +1,9 @@ +This extension can be used if `\-\-protocol icmp' is specified. It +provides the following option: +.TP +[\fB!\fP] \fB\-\-icmp\-type\fP {\fItype\fP[\fB/\fP\fIcode\fP]|\fItypename\fP} +This allows specification of the ICMP type, which can be a numeric +ICMP type, type/code pair, or one of the ICMP type names shown by the command +.nf + iptables \-p icmp \-h +.fi diff --git a/extensions/libipt_realm.c b/extensions/libipt_realm.c new file mode 100644 index 0000000..cd4b324 --- /dev/null +++ b/extensions/libipt_realm.c @@ -0,0 +1,253 @@ +/* Shared library add-on to iptables to add realm matching support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <ctype.h> +#include <getopt.h> +#if defined(__GLIBC__) && __GLIBC__ == 2 +#include <net/ethernet.h> +#else +#include <linux/if_ether.h> +#endif +#include <xtables.h> +#include <linux/netfilter_ipv4/ipt_realm.h> + +static void realm_help(void) +{ + printf( +"realm match options:\n" +"[!] --realm value[/mask]\n" +" Match realm\n"); +} + +static const struct option realm_opts[] = { + { "realm", 1, NULL, '1' }, + { .name = NULL } +}; + +struct realmname { + int id; + char* name; + int len; + struct realmname* next; +}; + +/* array of realms from /etc/iproute2/rt_realms */ +static struct realmname *realms; +/* 1 if loading failed */ +static int rdberr; + +static void load_realms(void) +{ + const char* rfnm = "/etc/iproute2/rt_realms"; + char buf[512]; + FILE *fil; + char *cur, *nxt; + int id; + struct realmname *oldnm = NULL, *newnm = NULL; + + fil = fopen(rfnm, "r"); + if (!fil) { + rdberr = 1; + return; + } + + while (fgets(buf, sizeof(buf), fil)) { + cur = buf; + while ((*cur == ' ') || (*cur == '\t')) + cur++; + if ((*cur == '#') || (*cur == '\n') || (*cur == 0)) + continue; + + /* iproute2 allows hex and dec format */ + errno = 0; + id = strtoul(cur, &nxt, strncmp(cur, "0x", 2) ? 10 : 16); + if ((nxt == cur) || errno) + continue; + + /* same boundaries as in iproute2 */ + if (id < 0 || id > 255) + continue; + cur = nxt; + + if (!isspace(*cur)) + continue; + while ((*cur == ' ') || (*cur == '\t')) + cur++; + if ((*cur == '#') || (*cur == '\n') || (*cur == 0)) + continue; + nxt = cur; + while ((*nxt != 0) && !isspace(*nxt)) + nxt++; + if (nxt == cur) + continue; + + /* found valid data */ + newnm = malloc(sizeof(struct realmname)); + if (newnm == NULL) { + perror("libipt_realm: malloc failed"); + exit(1); + } + newnm->id = id; + newnm->len = nxt - cur; + newnm->name = malloc(newnm->len + 1); + if (newnm->name == NULL) { + perror("libipt_realm: malloc failed"); + exit(1); + } + strncpy(newnm->name, cur, newnm->len); + newnm->name[newnm->len] = 0; + newnm->next = NULL; + + if (oldnm) + oldnm->next = newnm; + else + realms = newnm; + oldnm = newnm; + } + + fclose(fil); +} + +/* get realm id for name, -1 if error/not found */ +static int realm_name2id(const char* name) +{ + struct realmname* cur; + + if ((realms == NULL) && (rdberr == 0)) + load_realms(); + cur = realms; + if (cur == NULL) + return -1; + while (cur) { + if (!strncmp(name, cur->name, cur->len + 1)) + return cur->id; + cur = cur->next; + } + return -1; +} + +/* get realm name for id, NULL if error/not found */ +static const char *realm_id2name(int id) +{ + struct realmname* cur; + + if ((realms == NULL) && (rdberr == 0)) + load_realms(); + cur = realms; + if (cur == NULL) + return NULL; + while (cur) { + if (id == cur->id) + return cur->name; + cur = cur->next; + } + return NULL; +} + +static int realm_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ipt_realm_info *realminfo = (struct ipt_realm_info *)(*match)->data; + int id; + + switch (c) { + char *end; + case '1': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + end = optarg = optarg; + realminfo->id = strtoul(optarg, &end, 0); + if (end != optarg && (*end == '/' || *end == '\0')) { + if (*end == '/') + realminfo->mask = strtoul(end+1, &end, 0); + else + realminfo->mask = 0xffffffff; + if (*end != '\0' || end == optarg) + xtables_error(PARAMETER_PROBLEM, + "Bad realm value `%s'", optarg); + } else { + id = realm_name2id(optarg); + if (id == -1) + xtables_error(PARAMETER_PROBLEM, + "Realm `%s' not found", optarg); + realminfo->id = id; + realminfo->mask = 0xffffffff; + } + if (invert) + realminfo->invert = 1; + *flags = 1; + break; + + default: + return 0; + } + return 1; +} + +static void +print_realm(unsigned long id, unsigned long mask, int numeric) +{ + const char* name = NULL; + + if (mask != 0xffffffff) + printf("0x%lx/0x%lx ", id, mask); + else { + if (numeric == 0) + name = realm_id2name(id); + if (name) + printf("%s ", name); + else + printf("0x%lx ", id); + } +} + +static void realm_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ipt_realm_info *ri = (const void *)match->data; + + if (ri->invert) + printf("! "); + + printf("realm "); + print_realm(ri->id, ri->mask, numeric); +} + +static void realm_save(const void *ip, const struct xt_entry_match *match) +{ + const struct ipt_realm_info *ri = (const void *)match->data; + + if (ri->invert) + printf("! "); + + printf("--realm "); + print_realm(ri->id, ri->mask, 0); +} + +static void realm_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "realm match: You must specify `--realm'"); +} + +static struct xtables_match realm_mt_reg = { + .name = "realm", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct ipt_realm_info)), + .userspacesize = XT_ALIGN(sizeof(struct ipt_realm_info)), + .help = realm_help, + .parse = realm_parse, + .final_check = realm_check, + .print = realm_print, + .save = realm_save, + .extra_opts = realm_opts, +}; + +void _init(void) +{ + xtables_register_match(&realm_mt_reg); +} diff --git a/extensions/libipt_realm.man b/extensions/libipt_realm.man new file mode 100644 index 0000000..a40b1ad --- /dev/null +++ b/extensions/libipt_realm.man @@ -0,0 +1,7 @@ +This matches the routing realm. Routing realms are used in complex routing +setups involving dynamic routing protocols like BGP. +.TP +[\fB!\fP] \fB\-\-realm\fP \fIvalue\fP[\fB/\fP\fImask\fP] +Matches a given realm number (and optionally mask). If not a number, value +can be a named realm from /etc/iproute2/rt_realms (mask can not be used in +that case). diff --git a/extensions/libipt_ttl.c b/extensions/libipt_ttl.c new file mode 100644 index 0000000..e2fbcd5 --- /dev/null +++ b/extensions/libipt_ttl.c @@ -0,0 +1,167 @@ +/* Shared library add-on to iptables to add TTL matching support + * (C) 2000 by Harald Welte <laforge@gnumonks.org> + * + * $Id$ + * + * This program is released under the terms of GNU GPL */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <xtables.h> + +#include <linux/netfilter_ipv4/ipt_ttl.h> + +static void ttl_help(void) +{ + printf( +"ttl match options:\n" +" --ttl-eq value Match time to live value\n" +" --ttl-lt value Match TTL < value\n" +" --ttl-gt value Match TTL > value\n"); +} + +static int ttl_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ipt_ttl_info *info = (struct ipt_ttl_info *) (*match)->data; + unsigned int value; + + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + switch (c) { + case '2': + if (!xtables_strtoui(optarg, NULL, &value, 0, UINT8_MAX)) + xtables_error(PARAMETER_PROBLEM, + "ttl: Expected value between 0 and 255"); + + if (invert) + info->mode = IPT_TTL_NE; + else + info->mode = IPT_TTL_EQ; + + /* is 0 allowed? */ + info->ttl = value; + break; + case '3': + if (!xtables_strtoui(optarg, NULL, &value, 0, UINT8_MAX)) + xtables_error(PARAMETER_PROBLEM, + "ttl: Expected value between 0 and 255"); + + if (invert) + xtables_error(PARAMETER_PROBLEM, + "ttl: unexpected `!'"); + + info->mode = IPT_TTL_LT; + info->ttl = value; + break; + case '4': + if (!xtables_strtoui(optarg, NULL, &value, 0, UINT8_MAX)) + xtables_error(PARAMETER_PROBLEM, + "ttl: Expected value between 0 and 255"); + + if (invert) + xtables_error(PARAMETER_PROBLEM, + "ttl: unexpected `!'"); + + info->mode = IPT_TTL_GT; + info->ttl = value; + break; + default: + return 0; + + } + + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "Can't specify TTL option twice"); + *flags = 1; + + return 1; +} + +static void ttl_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "TTL match: You must specify one of " + "`--ttl-eq', `--ttl-lt', `--ttl-gt"); +} + +static void ttl_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ipt_ttl_info *info = + (struct ipt_ttl_info *) match->data; + + printf("TTL match "); + switch (info->mode) { + case IPT_TTL_EQ: + printf("TTL == "); + break; + case IPT_TTL_NE: + printf("TTL != "); + break; + case IPT_TTL_LT: + printf("TTL < "); + break; + case IPT_TTL_GT: + printf("TTL > "); + break; + } + printf("%u ", info->ttl); +} + +static void ttl_save(const void *ip, const struct xt_entry_match *match) +{ + const struct ipt_ttl_info *info = + (struct ipt_ttl_info *) match->data; + + switch (info->mode) { + case IPT_TTL_EQ: + printf("--ttl-eq "); + break; + case IPT_TTL_NE: + printf("! --ttl-eq "); + break; + case IPT_TTL_LT: + printf("--ttl-lt "); + break; + case IPT_TTL_GT: + printf("--ttl-gt "); + break; + default: + /* error */ + break; + } + printf("%u ", info->ttl); +} + +static const struct option ttl_opts[] = { + { "ttl", 1, NULL, '2' }, + { "ttl-eq", 1, NULL, '2'}, + { "ttl-lt", 1, NULL, '3'}, + { "ttl-gt", 1, NULL, '4'}, + { .name = NULL } +}; + +static struct xtables_match ttl_mt_reg = { + .name = "ttl", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct ipt_ttl_info)), + .userspacesize = XT_ALIGN(sizeof(struct ipt_ttl_info)), + .help = ttl_help, + .parse = ttl_parse, + .final_check = ttl_check, + .print = ttl_print, + .save = ttl_save, + .extra_opts = ttl_opts, +}; + + +void _init(void) +{ + xtables_register_match(&ttl_mt_reg); +} diff --git a/extensions/libipt_ttl.man b/extensions/libipt_ttl.man new file mode 100644 index 0000000..849f704 --- /dev/null +++ b/extensions/libipt_ttl.man @@ -0,0 +1,10 @@ +This module matches the time to live field in the IP header. +.TP +\fB\-\-ttl\-eq\fP \fIttl\fP +Matches the given TTL value. +.TP +\fB\-\-ttl\-gt\fP \fIttl\fP +Matches if TTL is greater than the given TTL value. +.TP +\fB\-\-ttl\-lt\fP \fIttl\fP +Matches if TTL is less than the given TTL value. diff --git a/extensions/libipt_unclean.c b/extensions/libipt_unclean.c new file mode 100644 index 0000000..bc4a4a0 --- /dev/null +++ b/extensions/libipt_unclean.c @@ -0,0 +1,15 @@ +/* Shared library add-on to iptables for unclean. */ +#include <xtables.h> + +static struct xtables_match unclean_mt_reg = { + .name = "unclean", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(0), + .userspacesize = XT_ALIGN(0), +}; + +void _init(void) +{ + xtables_register_match(&unclean_mt_reg); +} diff --git a/extensions/libipt_unclean.man b/extensions/libipt_unclean.man new file mode 100644 index 0000000..3fecd55 --- /dev/null +++ b/extensions/libipt_unclean.man @@ -0,0 +1,2 @@ +This module takes no options, but attempts to match packets which seem +malformed or unusual. This is regarded as experimental. diff --git a/extensions/libxt_CLASSIFY.c b/extensions/libxt_CLASSIFY.c new file mode 100644 index 0000000..82b8f4e --- /dev/null +++ b/extensions/libxt_CLASSIFY.c @@ -0,0 +1,115 @@ +/* Shared library add-on to iptables to add CLASSIFY target support. */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_CLASSIFY.h> +#include <linux/types.h> +#include <linux/pkt_sched.h> + +static void +CLASSIFY_help(void) +{ + printf( +"CLASSIFY target options:\n" +"--set-class MAJOR:MINOR Set skb->priority value (always hexadecimal!)\n"); +} + +static const struct option CLASSIFY_opts[] = { + { "set-class", 1, NULL, '1' }, + { .name = NULL } +}; + +static int CLASSIFY_string_to_priority(const char *s, unsigned int *p) +{ + unsigned int i, j; + + if (sscanf(s, "%x:%x", &i, &j) != 2) + return 1; + + *p = TC_H_MAKE(i<<16, j); + return 0; +} + +static int +CLASSIFY_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, + struct xt_entry_target **target) +{ + struct xt_classify_target_info *clinfo + = (struct xt_classify_target_info *)(*target)->data; + + switch (c) { + case '1': + if (CLASSIFY_string_to_priority(optarg, &clinfo->priority)) + xtables_error(PARAMETER_PROBLEM, + "Bad class value `%s'", optarg); + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "CLASSIFY: Can't specify --set-class twice"); + *flags = 1; + break; + + default: + return 0; + } + + return 1; +} + +static void +CLASSIFY_final_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "CLASSIFY: Parameter --set-class is required"); +} + +static void +CLASSIFY_print_class(unsigned int priority, int numeric) +{ + printf("%x:%x ", TC_H_MAJ(priority)>>16, TC_H_MIN(priority)); +} + +static void +CLASSIFY_print(const void *ip, + const struct xt_entry_target *target, + int numeric) +{ + const struct xt_classify_target_info *clinfo = + (const struct xt_classify_target_info *)target->data; + printf("CLASSIFY set "); + CLASSIFY_print_class(clinfo->priority, numeric); +} + +static void +CLASSIFY_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_classify_target_info *clinfo = + (const struct xt_classify_target_info *)target->data; + + printf("--set-class %.4x:%.4x ", + TC_H_MAJ(clinfo->priority)>>16, TC_H_MIN(clinfo->priority)); +} + +static struct xtables_target classify_target = { + .family = NFPROTO_UNSPEC, + .name = "CLASSIFY", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_classify_target_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_classify_target_info)), + .help = CLASSIFY_help, + .parse = CLASSIFY_parse, + .final_check = CLASSIFY_final_check, + .print = CLASSIFY_print, + .save = CLASSIFY_save, + .extra_opts = CLASSIFY_opts, +}; + +void _init(void) +{ + xtables_register_target(&classify_target); +} diff --git a/extensions/libxt_CLASSIFY.man b/extensions/libxt_CLASSIFY.man new file mode 100644 index 0000000..0270fd1 --- /dev/null +++ b/extensions/libxt_CLASSIFY.man @@ -0,0 +1,5 @@ +This module allows you to set the skb\->priority value (and thus classify the packet into a specific CBQ class). +.TP +\fB\-\-set\-class\fP \fImajor\fP\fB:\fP\fIminor\fP +Set the major and minor class value. The values are always interpreted as +hexadecimal even if no 0x prefix is given. diff --git a/extensions/libxt_CONNMARK.c b/extensions/libxt_CONNMARK.c new file mode 100644 index 0000000..6aba5f3 --- /dev/null +++ b/extensions/libxt_CONNMARK.c @@ -0,0 +1,445 @@ +/* Shared library add-on to iptables to add CONNMARK target support. + * + * (C) 2002,2004 MARA Systems AB <http://www.marasystems.com> + * by Henrik Nordstrom <hno@marasystems.com> + * + * Version 1.1 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_CONNMARK.h> + +struct xt_connmark_target_info { + unsigned long mark; + unsigned long mask; + u_int8_t mode; +}; + +enum { + F_MARK = 1 << 0, + F_SR_MARK = 1 << 1, +}; + +static void CONNMARK_help(void) +{ + printf( +"CONNMARK target options:\n" +" --set-mark value[/mask] Set conntrack mark value\n" +" --save-mark [--mask mask] Save the packet nfmark in the connection\n" +" --restore-mark [--mask mask] Restore saved nfmark value\n"); +} + +static const struct option CONNMARK_opts[] = { + { "set-mark", 1, NULL, '1' }, + { "save-mark", 0, NULL, '2' }, + { "restore-mark", 0, NULL, '3' }, + { "mask", 1, NULL, '4' }, + { .name = NULL } +}; + +static const struct option connmark_tg_opts[] = { + {.name = "set-xmark", .has_arg = true, .val = '='}, + {.name = "set-mark", .has_arg = true, .val = '-'}, + {.name = "and-mark", .has_arg = true, .val = '&'}, + {.name = "or-mark", .has_arg = true, .val = '|'}, + {.name = "xor-mark", .has_arg = true, .val = '^'}, + {.name = "save-mark", .has_arg = false, .val = 'S'}, + {.name = "restore-mark", .has_arg = false, .val = 'R'}, + {.name = "ctmask", .has_arg = true, .val = 'c'}, + {.name = "nfmask", .has_arg = true, .val = 'n'}, + {.name = "mask", .has_arg = true, .val = 'm'}, + {.name = NULL}, +}; + +static void connmark_tg_help(void) +{ + printf( +"CONNMARK target options:\n" +" --set-xmark value[/ctmask] Zero mask bits and XOR ctmark with value\n" +" --save-mark [--ctmask mask] [--nfmask mask]\n" +" Copy ctmark to nfmark using masks\n" +" --restore-mark [--ctmask mask] [--nfmask mask]\n" +" Copy nfmark to ctmark using masks\n" +" --set-mark value[/mask] Set conntrack mark value\n" +" --save-mark [--mask mask] Save the packet nfmark in the connection\n" +" --restore-mark [--mask mask] Restore saved nfmark value\n" +" --and-mark value Binary AND the ctmark with bits\n" +" --or-mark value Binary OR the ctmark with bits\n" +" --xor-mark value Binary XOR the ctmark with bits\n" +); +} + +static void connmark_tg_init(struct xt_entry_target *target) +{ + struct xt_connmark_tginfo1 *info = (void *)target->data; + + /* + * Need these defaults for --save-mark/--restore-mark if no + * --ctmark or --nfmask is given. + */ + info->ctmask = UINT32_MAX; + info->nfmask = UINT32_MAX; +} + +static int +CONNMARK_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_connmark_target_info *markinfo + = (struct xt_connmark_target_info *)(*target)->data; + + switch (c) { + char *end; + case '1': + markinfo->mode = XT_CONNMARK_SET; + + markinfo->mark = strtoul(optarg, &end, 0); + if (*end == '/' && end[1] != '\0') + markinfo->mask = strtoul(end+1, &end, 0); + + if (*end != '\0' || end == optarg) + xtables_error(PARAMETER_PROBLEM, "Bad MARK value \"%s\"", optarg); + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "CONNMARK target: Can't specify --set-mark twice"); + *flags = 1; + break; + case '2': + markinfo->mode = XT_CONNMARK_SAVE; + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "CONNMARK target: Can't specify --save-mark twice"); + *flags = 1; + break; + case '3': + markinfo->mode = XT_CONNMARK_RESTORE; + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "CONNMARK target: Can't specify --restore-mark twice"); + *flags = 1; + break; + case '4': + if (!*flags) + xtables_error(PARAMETER_PROBLEM, + "CONNMARK target: Can't specify --mask without a operation"); + markinfo->mask = strtoul(optarg, &end, 0); + + if (*end != '\0' || end == optarg) + xtables_error(PARAMETER_PROBLEM, "Bad MASK value \"%s\"", optarg); + break; + default: + return 0; + } + + return 1; +} + +static int connmark_tg_parse(int c, char **argv, int invert, + unsigned int *flags, const void *entry, + struct xt_entry_target **target) +{ + struct xt_connmark_tginfo1 *info = (void *)(*target)->data; + unsigned int value, mask = UINT32_MAX; + char *end; + + switch (c) { + case '=': /* --set-xmark */ + case '-': /* --set-mark */ + xtables_param_act(XTF_ONE_ACTION, "CONNMARK", *flags & F_MARK); + if (!xtables_strtoui(optarg, &end, &value, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "CONNMARK", "--set-xmark/--set-mark", optarg); + if (*end == '/') + if (!xtables_strtoui(end + 1, &end, &mask, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "CONNMARK", "--set-xmark/--set-mark", optarg); + if (*end != '\0') + xtables_param_act(XTF_BAD_VALUE, "CONNMARK", "--set-xmark/--set-mark", optarg); + info->mode = XT_CONNMARK_SET; + info->ctmark = value; + info->ctmask = mask; + if (c == '-') + info->ctmask |= value; + *flags |= F_MARK; + return true; + + case '&': /* --and-mark */ + xtables_param_act(XTF_ONE_ACTION, "CONNMARK", *flags & F_MARK); + if (!xtables_strtoui(optarg, NULL, &mask, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "CONNMARK", "--and-mark", optarg); + info->mode = XT_CONNMARK_SET; + info->ctmark = 0; + info->ctmask = ~mask; + *flags |= F_MARK; + return true; + + case '|': /* --or-mark */ + xtables_param_act(XTF_ONE_ACTION, "CONNMARK", *flags & F_MARK); + if (!xtables_strtoui(optarg, NULL, &value, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "CONNMARK", "--or-mark", optarg); + info->mode = XT_CONNMARK_SET; + info->ctmark = value; + info->ctmask = value; + *flags |= F_MARK; + return true; + + case '^': /* --xor-mark */ + xtables_param_act(XTF_ONE_ACTION, "CONNMARK", *flags & F_MARK); + if (!xtables_strtoui(optarg, NULL, &value, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "CONNMARK", "--xor-mark", optarg); + info->mode = XT_CONNMARK_SET; + info->ctmark = value; + info->ctmask = 0; + *flags |= F_MARK; + return true; + + case 'S': /* --save-mark */ + xtables_param_act(XTF_ONE_ACTION, "CONNMARK", *flags & F_MARK); + info->mode = XT_CONNMARK_SAVE; + *flags |= F_MARK | F_SR_MARK; + return true; + + case 'R': /* --restore-mark */ + xtables_param_act(XTF_ONE_ACTION, "CONNMARK", *flags & F_MARK); + info->mode = XT_CONNMARK_RESTORE; + *flags |= F_MARK | F_SR_MARK; + return true; + + case 'n': /* --nfmask */ + if (!(*flags & F_SR_MARK)) + xtables_error(PARAMETER_PROBLEM, "CONNMARK: --save-mark " + "or --restore-mark is required for " + "--nfmask"); + if (!xtables_strtoui(optarg, NULL, &value, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "CONNMARK", "--nfmask", optarg); + info->nfmask = value; + return true; + + case 'c': /* --ctmask */ + if (!(*flags & F_SR_MARK)) + xtables_error(PARAMETER_PROBLEM, "CONNMARK: --save-mark " + "or --restore-mark is required for " + "--ctmask"); + if (!xtables_strtoui(optarg, NULL, &value, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "CONNMARK", "--ctmask", optarg); + info->ctmask = value; + return true; + + case 'm': /* --mask */ + if (!(*flags & F_SR_MARK)) + xtables_error(PARAMETER_PROBLEM, "CONNMARK: --save-mark " + "or --restore-mark is required for " + "--mask"); + if (!xtables_strtoui(optarg, NULL, &value, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "CONNMARK", "--mask", optarg); + info->nfmask = info->ctmask = value; + return true; + } + + return false; +} + +static void connmark_tg_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "CONNMARK target: No operation specified"); +} + +static void +print_mark(unsigned long mark) +{ + printf("0x%lx", mark); +} + +static void +print_mask(const char *text, unsigned long mask) +{ + if (mask != 0xffffffffUL) + printf("%s0x%lx", text, mask); +} + +static void CONNMARK_print(const void *ip, + const struct xt_entry_target *target, int numeric) +{ + const struct xt_connmark_target_info *markinfo = + (const struct xt_connmark_target_info *)target->data; + switch (markinfo->mode) { + case XT_CONNMARK_SET: + printf("CONNMARK set "); + print_mark(markinfo->mark); + print_mask("/", markinfo->mask); + printf(" "); + break; + case XT_CONNMARK_SAVE: + printf("CONNMARK save "); + print_mask("mask ", markinfo->mask); + printf(" "); + break; + case XT_CONNMARK_RESTORE: + printf("CONNMARK restore "); + print_mask("mask ", markinfo->mask); + break; + default: + printf("ERROR: UNKNOWN CONNMARK MODE "); + break; + } +} + +static void +connmark_tg_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_connmark_tginfo1 *info = (const void *)target->data; + + switch (info->mode) { + case XT_CONNMARK_SET: + if (info->ctmark == 0) + printf("CONNMARK and 0x%x ", + (unsigned int)(u_int32_t)~info->ctmask); + else if (info->ctmark == info->ctmask) + printf("CONNMARK or 0x%x ", info->ctmark); + else if (info->ctmask == 0) + printf("CONNMARK xor 0x%x ", info->ctmark); + else if (info->ctmask == 0xFFFFFFFFU) + printf("CONNMARK set 0x%x ", info->ctmark); + else + printf("CONNMARK xset 0x%x/0x%x ", + info->ctmark, info->ctmask); + break; + case XT_CONNMARK_SAVE: + if (info->nfmask == UINT32_MAX && info->ctmask == UINT32_MAX) + printf("CONNMARK save "); + else if (info->nfmask == info->ctmask) + printf("CONNMARK save mask 0x%x ", info->nfmask); + else + printf("CONNMARK save nfmask 0x%x ctmask ~0x%x ", + info->nfmask, info->ctmask); + break; + case XT_CONNMARK_RESTORE: + if (info->ctmask == UINT32_MAX && info->nfmask == UINT32_MAX) + printf("CONNMARK restore "); + else if (info->ctmask == info->nfmask) + printf("CONNMARK restore mask 0x%x ", info->ctmask); + else + printf("CONNMARK restore ctmask 0x%x nfmask ~0x%x ", + info->ctmask, info->nfmask); + break; + + default: + printf("ERROR: UNKNOWN CONNMARK MODE"); + break; + } +} + +static void CONNMARK_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_connmark_target_info *markinfo = + (const struct xt_connmark_target_info *)target->data; + + switch (markinfo->mode) { + case XT_CONNMARK_SET: + printf("--set-mark "); + print_mark(markinfo->mark); + print_mask("/", markinfo->mask); + printf(" "); + break; + case XT_CONNMARK_SAVE: + printf("--save-mark "); + print_mask("--mask ", markinfo->mask); + break; + case XT_CONNMARK_RESTORE: + printf("--restore-mark "); + print_mask("--mask ", markinfo->mask); + break; + default: + printf("ERROR: UNKNOWN CONNMARK MODE "); + break; + } +} + +static void CONNMARK_init(struct xt_entry_target *t) +{ + struct xt_connmark_target_info *markinfo + = (struct xt_connmark_target_info *)t->data; + + markinfo->mask = 0xffffffffUL; +} + +static void +connmark_tg_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_connmark_tginfo1 *info = (const void *)target->data; + + switch (info->mode) { + case XT_CONNMARK_SET: + printf("--set-xmark 0x%x/0x%x ", info->ctmark, info->ctmask); + break; + case XT_CONNMARK_SAVE: + printf("--save-mark --nfmask 0x%x --ctmask 0x%x ", + info->nfmask, info->ctmask); + break; + case XT_CONNMARK_RESTORE: + printf("--restore-mark --nfmask 0x%x --ctmask 0x%x ", + info->nfmask, info->ctmask); + break; + default: + printf("ERROR: UNKNOWN CONNMARK MODE"); + break; + } +} + +static struct xtables_target connmark_tg_reg[] = { + { + .family = NFPROTO_UNSPEC, + .name = "CONNMARK", + .revision = 0, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_connmark_target_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_connmark_target_info)), + .help = CONNMARK_help, + .init = CONNMARK_init, + .parse = CONNMARK_parse, + .final_check = connmark_tg_check, + .print = CONNMARK_print, + .save = CONNMARK_save, + .extra_opts = CONNMARK_opts, + }, + { + .version = XTABLES_VERSION, + .name = "CONNMARK", + .revision = 1, + .family = NFPROTO_UNSPEC, + .size = XT_ALIGN(sizeof(struct xt_connmark_tginfo1)), + .userspacesize = XT_ALIGN(sizeof(struct xt_connmark_tginfo1)), + .help = connmark_tg_help, + .init = connmark_tg_init, + .parse = connmark_tg_parse, + .final_check = connmark_tg_check, + .print = connmark_tg_print, + .save = connmark_tg_save, + .extra_opts = connmark_tg_opts, + }, +}; + +void _init(void) +{ + xtables_register_targets(connmark_tg_reg, ARRAY_SIZE(connmark_tg_reg)); +} diff --git a/extensions/libxt_CONNMARK.man b/extensions/libxt_CONNMARK.man new file mode 100644 index 0000000..13c6b4b --- /dev/null +++ b/extensions/libxt_CONNMARK.man @@ -0,0 +1,53 @@ +This module sets the netfilter mark value associated with a connection. The +mark is 32 bits wide. +.TP +\fB\-\-set\-xmark\fP \fIvalue\fP[\fB/\fP\fImask\fP] +Zero out the bits given by \fImask\fR and XOR \fIvalue\fR into the ctmark. +.TP +\fB\-\-save\-mark\fP [\fB\-\-nfmask\fP \fInfmask\fP] [\fB\-\-ctmask\fP \fIctmask\fP] +Copy the packet mark (nfmark) to the connection mark (ctmark) using the given +masks. The new nfmark value is determined as follows: +.IP +ctmark = (ctmark & ~ctmask) ^ (nfmark & nfmask) +.IP +i.e. \fIctmask\fR defines what bits to clear and \fInfmask\fR what bits of the +nfmark to XOR into the ctmark. \fIctmask\fR and \fInfmask\fR default to +0xFFFFFFFF. +.TP +\fB\-\-restore\-mark\fP [\fB\-\-nfmask\fP \fInfmask\fP] [\fB\-\-ctmask\fP \fIctmask\fP] +Copy the connection mark (ctmark) to the packet mark (nfmark) using the given +masks. The new ctmark value is determined as follows: +.IP +nfmark = (nfmark & ~\fInfmask\fR) ^ (ctmark & \fIctmask\fR); +.IP +i.e. \fInfmask\fR defines what bits to clear and \fIctmask\fR what bits of the +ctmark to XOR into the nfmark. \fIctmask\fR and \fInfmask\fR default to +0xFFFFFFFF. +.IP +\fB\-\-restore\-mark\fP is only valid in the \fBmangle\fP table. +.PP +The following mnemonics are available for \fB\-\-set\-xmark\fP: +.TP +\fB\-\-and\-mark\fP \fIbits\fP +Binary AND the ctmark with \fIbits\fR. (Mnemonic for \fB\-\-set\-xmark +0/\fR\fIinvbits\fR, where \fIinvbits\fR is the binary negation of \fIbits\fR.) +.TP +\fB\-\-or\-mark\fP \fIbits\fP +Binary OR the ctmark with \fIbits\fR. (Mnemonic for \fB\-\-set\-xmark\fP +\fIbits\fR\fB/\fR\fIbits\fR.) +.TP +\fB\-\-xor\-mark\fP \fIbits\fP +Binary XOR the ctmark with \fIbits\fR. (Mnemonic for \fB\-\-set\-xmark\fP +\fIbits\fR\fB/0\fR.) +.TP +\fB\-\-set\-mark\fP \fIvalue\fP[\fB/\fP\fImask\fP] +Set the connection mark. If a mask is specified then only those bits set in the +mask are modified. +.TP +\fB\-\-save\-mark\fP [\fB\-\-mask\fP \fImask\fP] +Copy the nfmark to the ctmark. If a mask is specified, only those bits are +copied. +.TP +\fB\-\-restore\-mark\fP [\fB\-\-mask\fP \fImask\fP] +Copy the ctmark to the nfmark. If a mask is specified, only those bits are +copied. This is only valid in the \fBmangle\fR table. diff --git a/extensions/libxt_CONNSECMARK.c b/extensions/libxt_CONNSECMARK.c new file mode 100644 index 0000000..d95339f --- /dev/null +++ b/extensions/libxt_CONNSECMARK.c @@ -0,0 +1,127 @@ +/* + * Shared library add-on to iptables to add CONNSECMARK target support. + * + * Based on the MARK and CONNMARK targets. + * + * Copyright (C) 2006 Red Hat, Inc., James Morris <jmorris@redhat.com> + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <linux/netfilter/xt_CONNSECMARK.h> + +#define PFX "CONNSECMARK target: " + +static void CONNSECMARK_help(void) +{ + printf( +"CONNSECMARK target options:\n" +" --save Copy security mark from packet to conntrack\n" +" --restore Copy security mark from connection to packet\n"); +} + +static const struct option CONNSECMARK_opts[] = { + { "save", 0, NULL, '1' }, + { "restore", 0, NULL, '2' }, + { .name = NULL } +}; + +static int +CONNSECMARK_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_connsecmark_target_info *info = + (struct xt_connsecmark_target_info*)(*target)->data; + + switch (c) { + case '1': + if (*flags & CONNSECMARK_SAVE) + xtables_error(PARAMETER_PROBLEM, PFX + "Can't specify --save twice"); + info->mode = CONNSECMARK_SAVE; + *flags |= CONNSECMARK_SAVE; + break; + + case '2': + if (*flags & CONNSECMARK_RESTORE) + xtables_error(PARAMETER_PROBLEM, PFX + "Can't specify --restore twice"); + info->mode = CONNSECMARK_RESTORE; + *flags |= CONNSECMARK_RESTORE; + break; + + default: + return 0; + } + + return 1; +} + +static void CONNSECMARK_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, PFX "parameter required"); + + if (flags == (CONNSECMARK_SAVE|CONNSECMARK_RESTORE)) + xtables_error(PARAMETER_PROBLEM, PFX "only one flag of --save " + "or --restore is allowed"); +} + +static void print_connsecmark(const struct xt_connsecmark_target_info *info) +{ + switch (info->mode) { + case CONNSECMARK_SAVE: + printf("save "); + break; + + case CONNSECMARK_RESTORE: + printf("restore "); + break; + + default: + xtables_error(OTHER_PROBLEM, PFX "invalid mode %hhu\n", info->mode); + } +} + +static void +CONNSECMARK_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_connsecmark_target_info *info = + (struct xt_connsecmark_target_info*)(target)->data; + + printf("CONNSECMARK "); + print_connsecmark(info); +} + +static void +CONNSECMARK_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_connsecmark_target_info *info = + (struct xt_connsecmark_target_info*)target->data; + + printf("--"); + print_connsecmark(info); +} + +static struct xtables_target connsecmark_target = { + .family = NFPROTO_UNSPEC, + .name = "CONNSECMARK", + .version = XTABLES_VERSION, + .revision = 0, + .size = XT_ALIGN(sizeof(struct xt_connsecmark_target_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_connsecmark_target_info)), + .parse = CONNSECMARK_parse, + .help = CONNSECMARK_help, + .final_check = CONNSECMARK_check, + .print = CONNSECMARK_print, + .save = CONNSECMARK_save, + .extra_opts = CONNSECMARK_opts, +}; + +void _init(void) +{ + xtables_register_target(&connsecmark_target); +} diff --git a/extensions/libxt_CONNSECMARK.man b/extensions/libxt_CONNSECMARK.man new file mode 100644 index 0000000..a72e710 --- /dev/null +++ b/extensions/libxt_CONNSECMARK.man @@ -0,0 +1,15 @@ +This module copies security markings from packets to connections +(if unlabeled), and from connections back to packets (also only +if unlabeled). Typically used in conjunction with SECMARK, it is +only valid in the +.B mangle +table. +.TP +\fB\-\-save\fP +If the packet has a security marking, copy it to the connection +if the connection is not marked. +.TP +\fB\-\-restore\fP +If the packet does not have a security marking, and the connection +does, copy the security marking from the connection to the packet. + diff --git a/extensions/libxt_CT.c b/extensions/libxt_CT.c new file mode 100644 index 0000000..6be6ea0 --- /dev/null +++ b/extensions/libxt_CT.c @@ -0,0 +1,192 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <getopt.h> +#include <xtables.h> +#include <linux/netfilter/nf_conntrack_common.h> +#include <linux/netfilter/xt_CT.h> + +static void ct_help(void) +{ + printf( +"CT target options:\n" +" --notrack Don't track connection\n" +" --helper name Use conntrack helper 'name' for connection\n" +" --ctevents event[,event...] Generate specified conntrack events for connection\n" +" --expevents event[,event...] Generate specified expectation events for connection\n" +" --zone ID Assign/Lookup connection in zone ID\n" + ); +} + +enum ct_options { + CT_OPT_NOTRACK = 0x1, + CT_OPT_HELPER = 0x2, + CT_OPT_CTEVENTS = 0x4, + CT_OPT_EXPEVENTS = 0x8, + CT_OPT_ZONE = 0x10, +}; + +static const struct option ct_opts[] = { + { "notrack", 0, NULL, CT_OPT_NOTRACK }, + { "helper", 1, NULL, CT_OPT_HELPER }, + { "ctevents", 1, NULL, CT_OPT_CTEVENTS }, + { "expevents", 1, NULL, CT_OPT_EXPEVENTS }, + { "zone", 1, NULL, CT_OPT_ZONE }, + { .name = NULL }, +}; + +struct event_tbl { + const char *name; + unsigned int event; +}; + +static const struct event_tbl ct_event_tbl[] = { + { "new", IPCT_NEW }, + { "related", IPCT_RELATED }, + { "destroy", IPCT_DESTROY }, + { "reply", IPCT_REPLY }, + { "assured", IPCT_ASSURED }, + { "protoinfo", IPCT_PROTOINFO }, + { "helper", IPCT_HELPER }, + { "mark", IPCT_MARK }, + { "natseqinfo", IPCT_NATSEQADJ }, + { "secmark", IPCT_SECMARK }, +}; + +static const struct event_tbl exp_event_tbl[] = { + { "new", IPEXP_NEW }, +}; + +static uint32_t ct_parse_events(const struct event_tbl *tbl, unsigned int size, + const char *events) +{ + char str[strlen(events) + 1], *e = str, *t; + unsigned int mask = 0, i; + + strcpy(str, events); + while ((t = strsep(&e, ","))) { + for (i = 0; i < size; i++) { + if (strcmp(t, tbl[i].name)) + continue; + mask |= 1 << tbl[i].event; + break; + } + + if (i == size) + xtables_error(PARAMETER_PROBLEM, "Unknown event type \"%s\"", t); + } + + return mask; +} + +static void ct_print_events(const char *pfx, const struct event_tbl *tbl, + unsigned int size, uint32_t mask) +{ + const char *sep = ""; + unsigned int i; + + printf("%s ", pfx); + for (i = 0; i < size; i++) { + if (mask & (1 << tbl[i].event)) { + printf("%s%s", sep, tbl[i].name); + sep = ","; + } + } + printf(" "); +} + +static int ct_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_ct_target_info *info = (struct xt_ct_target_info *)(*target)->data; + unsigned int zone; + + switch (c) { + case CT_OPT_NOTRACK: + xtables_param_act(XTF_ONLY_ONCE, "CT", "--notrack", *flags & CT_OPT_NOTRACK); + info->flags |= XT_CT_NOTRACK; + break; + case CT_OPT_HELPER: + xtables_param_act(XTF_ONLY_ONCE, "CT", "--helper", *flags & CT_OPT_HELPER); + strncpy(info->helper, optarg, sizeof(info->helper)); + info->helper[sizeof(info->helper) - 1] = '\0'; + break; + case CT_OPT_CTEVENTS: + xtables_param_act(XTF_ONLY_ONCE, "CT", "--ctevents", *flags & CT_OPT_CTEVENTS); + info->ct_events = ct_parse_events(ct_event_tbl, ARRAY_SIZE(ct_event_tbl), optarg); + break; + case CT_OPT_EXPEVENTS: + xtables_param_act(XTF_ONLY_ONCE, "CT", "--expevents", *flags & CT_OPT_EXPEVENTS); + info->exp_events = ct_parse_events(exp_event_tbl, ARRAY_SIZE(exp_event_tbl), optarg); + break; + case CT_OPT_ZONE: + xtables_param_act(XTF_ONLY_ONCE, "CT", "--zone", *flags & CT_OPT_ZONE); + if (!xtables_strtoui(optarg, NULL, &zone, 0, UINT16_MAX)) + xtables_error(PARAMETER_PROBLEM, "Bad zone value \"%s\"", optarg); + info->zone = zone; + break; + default: + return 0; + } + + *flags |= c; + return 1; +} + +static void ct_print(const void *ip, const struct xt_entry_target *target, int numeric) +{ + const struct xt_ct_target_info *info = + (const struct xt_ct_target_info *)target->data; + + printf("CT "); + if (info->flags & XT_CT_NOTRACK) + printf("notrack "); + if (info->helper[0]) + printf("helper %s ", info->helper); + if (info->ct_events) + ct_print_events("ctevents", ct_event_tbl, + ARRAY_SIZE(ct_event_tbl), info->ct_events); + if (info->exp_events) + ct_print_events("expevents", exp_event_tbl, + ARRAY_SIZE(exp_event_tbl), info->exp_events); + if (info->zone) + printf("zone %u ", info->zone); +} + +static void ct_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_ct_target_info *info = + (const struct xt_ct_target_info *)target->data; + + if (info->flags & XT_CT_NOTRACK) + printf("--notrack "); + if (info->helper[0]) + printf("--helper %s ", info->helper); + if (info->ct_events) + ct_print_events("--ctevents", ct_event_tbl, + ARRAY_SIZE(ct_event_tbl), info->ct_events); + if (info->exp_events) + ct_print_events("--expevents", exp_event_tbl, + ARRAY_SIZE(exp_event_tbl), info->exp_events); + if (info->zone) + printf("--zone %u ", info->zone); +} + +static struct xtables_target ct_target = { + .family = NFPROTO_UNSPEC, + .name = "CT", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_ct_target_info)), + .userspacesize = offsetof(struct xt_ct_target_info, ct), + .help = ct_help, + .parse = ct_parse, + .print = ct_print, + .save = ct_save, + .extra_opts = ct_opts, +}; + +void _init(void) +{ + xtables_register_target(&ct_target); +} diff --git a/extensions/libxt_CT.man b/extensions/libxt_CT.man new file mode 100644 index 0000000..ff258b7 --- /dev/null +++ b/extensions/libxt_CT.man @@ -0,0 +1,25 @@ +The CT target allows to set parameters for a packet or its associated +connection. The target attaches a "template" connection tracking entry to +the packet, which is then used by the conntrack core when initializing +a new ct entry. This target is thus only valid in the "raw" table. +.TP +\fB\-\-notrack\fP +Disables connection tracking for this packet. +.TP +\fB\-\-helper\fP \fIname\fP +Use the helper identified by \fIname\fP for the connection. This is more +flexible than loading the conntrack helper modules with preset ports. +.TP +\fB\-\-ctevents\fP \fIevent\fP[\fB,\fP...] +Only generate the specified conntrack events for this connection. Possible +event types are: \fBnew\fP, \fBrelated\fP, \fBdestroy\fP, \fBreply\fP, +\fBassured\fP, \fBprotoinfo\fP, \fBhelper\fP, \fBmark\fP (this refers to +the ctmark, not nfmark), \fBnatseqinfo\fP, \fBsecmark\fP (ctsecmark). +.TP +\fB\-\-expevents\fP \fIevent\fP[\fB,\fP...] +Only generate the specified expectation events for this connection. +Possible event types are: \fBnew\fP. +.TP +\fB\-\-zone\fP \fIid\fP +Assign this packet to zone \fIid\fP and only have lookups done in that zone. +By default, packets have zone 0. diff --git a/extensions/libxt_DSCP.c b/extensions/libxt_DSCP.c new file mode 100644 index 0000000..82ac10c --- /dev/null +++ b/extensions/libxt_DSCP.c @@ -0,0 +1,149 @@ +/* Shared library add-on to iptables for DSCP + * + * (C) 2000- 2002 by Matthew G. Marsh <mgm@paktronix.com>, + * Harald Welte <laforge@gnumonks.org> + * + * This program is distributed under the terms of GNU GPL v2, 1991 + * + * libipt_DSCP.c borrowed heavily from libipt_TOS.c + * + * --set-class added by Iain Barnes + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_DSCP.h> + +/* This is evil, but it's my code - HW*/ +#include "dscp_helper.c" + +static void DSCP_help(void) +{ + printf( +"DSCP target options\n" +" --set-dscp value Set DSCP field in packet header to value\n" +" This value can be in decimal (ex: 32)\n" +" or in hex (ex: 0x20)\n" +" --set-dscp-class class Set the DSCP field in packet header to the\n" +" value represented by the DiffServ class value.\n" +" This class may be EF,BE or any of the CSxx\n" +" or AFxx classes.\n" +"\n" +" These two options are mutually exclusive !\n" +); +} + +static const struct option DSCP_opts[] = { + { "set-dscp", 1, NULL, 'F' }, + { "set-dscp-class", 1, NULL, 'G' }, + { .name = NULL } +}; + +static void +parse_dscp(const char *s, struct xt_DSCP_info *dinfo) +{ + unsigned int dscp; + + if (!xtables_strtoui(s, NULL, &dscp, 0, UINT8_MAX)) + xtables_error(PARAMETER_PROBLEM, + "Invalid dscp `%s'\n", s); + + if (dscp > XT_DSCP_MAX) + xtables_error(PARAMETER_PROBLEM, + "DSCP `%d` out of range\n", dscp); + + dinfo->dscp = dscp; +} + + +static void +parse_class(const char *s, struct xt_DSCP_info *dinfo) +{ + unsigned int dscp = class_to_dscp(s); + + /* Assign the value */ + dinfo->dscp = dscp; +} + + +static int DSCP_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_DSCP_info *dinfo + = (struct xt_DSCP_info *)(*target)->data; + + switch (c) { + case 'F': + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "DSCP target: Only use --set-dscp ONCE!"); + parse_dscp(optarg, dinfo); + *flags = 1; + break; + case 'G': + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "DSCP target: Only use --set-dscp-class ONCE!"); + parse_class(optarg, dinfo); + *flags = 1; + break; + + default: + return 0; + } + + return 1; +} + +static void DSCP_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "DSCP target: Parameter --set-dscp is required"); +} + +static void +print_dscp(u_int8_t dscp, int numeric) +{ + printf("0x%02x ", dscp); +} + +static void DSCP_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_DSCP_info *dinfo = + (const struct xt_DSCP_info *)target->data; + printf("DSCP set "); + print_dscp(dinfo->dscp, numeric); +} + +static void DSCP_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_DSCP_info *dinfo = + (const struct xt_DSCP_info *)target->data; + + printf("--set-dscp 0x%02x ", dinfo->dscp); +} + +static struct xtables_target dscp_target = { + .family = NFPROTO_UNSPEC, + .name = "DSCP", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_DSCP_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_DSCP_info)), + .help = DSCP_help, + .parse = DSCP_parse, + .final_check = DSCP_check, + .print = DSCP_print, + .save = DSCP_save, + .extra_opts = DSCP_opts, +}; + +void _init(void) +{ + xtables_register_target(&dscp_target); +} diff --git a/extensions/libxt_DSCP.man b/extensions/libxt_DSCP.man new file mode 100644 index 0000000..551ba2e --- /dev/null +++ b/extensions/libxt_DSCP.man @@ -0,0 +1,9 @@ +This target allows to alter the value of the DSCP bits within the TOS +header of the IPv4 packet. As this manipulates a packet, it can only +be used in the mangle table. +.TP +\fB\-\-set\-dscp\fP \fIvalue\fP +Set the DSCP field to a numerical value (can be decimal or hex) +.TP +\fB\-\-set\-dscp\-class\fP \fIclass\fP +Set the DSCP field to a DiffServ class. diff --git a/extensions/libxt_LED.c b/extensions/libxt_LED.c new file mode 100644 index 0000000..af0e091 --- /dev/null +++ b/extensions/libxt_LED.c @@ -0,0 +1,155 @@ +/* + * libxt_LED.c - shared library add-on to iptables to add customized LED + * trigger support. + * + * (C) 2008 Adam Nielsen <a.nielsen@shikadi.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <stddef.h> + +#include <xtables.h> + +#include <linux/netfilter/xt_LED.h> + +static const struct option LED_opts[] = { + {.name = "led-trigger-id", .has_arg = true, .val = 'i'}, + {.name = "led-delay", .has_arg = true, .val = 'd'}, + {.name = "led-always-blink", .has_arg = false, .val = 'a'}, + {.name = NULL}, +}; + +static void LED_help(void) +{ + printf( +"LED target options:\n" +"--led-trigger-id name suffix for led trigger name\n" +"--led-delay ms leave the LED on for this number of\n" +" milliseconds after triggering.\n" +"--led-always-blink blink on arriving packets, even if\n" +" the LED is already on.\n" + ); +} + +static int LED_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_led_info *led = (void *)(*target)->data; + + switch (c) { + case 'i': + xtables_param_act(XTF_NO_INVERT, "LED", + "--led-trigger-id", invert); + if (strlen("netfilter-") + strlen(optarg) > sizeof(led->id)) + xtables_error(PARAMETER_PROBLEM, + "--led-trigger-id must be 16 chars or less"); + if (optarg[0] == '\0') + xtables_error(PARAMETER_PROBLEM, + "--led-trigger-id cannot be blank"); + + /* "netfilter-" + 16 char id == 26 == sizeof(led->id) */ + strcpy(led->id, "netfilter-"); + strcat(led->id, optarg); + *flags = 1; + return true; + + case 'd': + xtables_param_act(XTF_NO_INVERT, "LED", "--led-delay", invert); + if (strncasecmp(optarg, "inf", 3) == 0) + led->delay = -1; + else + led->delay = strtoul(optarg, NULL, 0); + + return true; + + case 'a': + if (!invert) + led->always_blink = 1; + return true; + } + return false; +} + +static void LED_final_check(unsigned int flags) +{ + if (flags == 0) + xtables_error(PARAMETER_PROBLEM, + "--led-trigger-id must be specified"); +} + +static void LED_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_led_info *led = (void *)target->data; + const char *id = led->id + strlen("netfilter-"); /* trim off prefix */ + + printf("led-trigger-id:\""); + /* Escape double quotes and backslashes in the ID */ + while (*id != '\0') { + if (*id == '"' || *id == '\\') + printf("\\"); + printf("%c", *id++); + } + printf("\" "); + + if (led->delay == -1) + printf("led-delay:inf "); + else + printf("led-delay:%dms ", led->delay); + + if (led->always_blink) + printf("led-always-blink "); +} + +static void LED_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_led_info *led = (void *)target->data; + const char *id = led->id + strlen("netfilter-"); /* trim off prefix */ + + printf("--led-trigger-id \""); + /* Escape double quotes and backslashes in the ID */ + while (*id != '\0') { + if (*id == '"' || *id == '\\') + printf("\\"); + printf("%c", *id++); + } + printf("\" "); + + /* Only print the delay if it's not zero (the default) */ + if (led->delay > 0) + printf("--led-delay %d ", led->delay); + else if (led->delay == -1) + printf("--led-delay inf "); + + /* Only print always_blink if it's not set to the default */ + if (led->always_blink) + printf("--led-always-blink "); +} + +static struct xtables_target led_tg_reg = { + .version = XTABLES_VERSION, + .name = "LED", + .family = PF_UNSPEC, + .revision = 0, + .size = XT_ALIGN(sizeof(struct xt_led_info)), + .userspacesize = offsetof(struct xt_led_info, internal_data), + .help = LED_help, + .parse = LED_parse, + .final_check = LED_final_check, + .extra_opts = LED_opts, + .print = LED_print, + .save = LED_save, +}; + +void _init(void) +{ + xtables_register_target(&led_tg_reg); +} diff --git a/extensions/libxt_LED.man b/extensions/libxt_LED.man new file mode 100644 index 0000000..81c2f29 --- /dev/null +++ b/extensions/libxt_LED.man @@ -0,0 +1,30 @@ +This creates an LED-trigger that can then be attached to system indicator +lights, to blink or illuminate them when certain packets pass through the +system. One example might be to light up an LED for a few minutes every time +an SSH connection is made to the local machine. The following options control +the trigger behavior: +.TP +\fB\-\-led\-trigger\-id\fP \fIname\fP +This is the name given to the LED trigger. The actual name of the trigger +will be prefixed with "netfilter-". +.TP +\fB\-\-led-delay\fP \fIms\fP +This indicates how long (in milliseconds) the LED should be left illuminated +when a packet arrives before being switched off again. The default is 0 +(blink as fast as possible.) The special value \fIinf\fP can be given to +leave the LED on permanently once activated. (In this case the trigger will +need to be manually detached and reattached to the LED device to switch it +off again.) +.TP +\fB\-\-led\-always\-blink\fP +Always make the LED blink on packet arrival, even if the LED is already on. +This allows notification of new packets even with long delay values (which +otherwise would result in a silent prolonging of the delay time.) +.TP +Example: +.TP +Create an LED trigger for incoming SSH traffic: +iptables \-A INPUT \-p tcp \-\-dport 22 \-j LED \-\-led\-trigger\-id ssh +.TP +Then attach the new trigger to an LED: +echo netfilter\-ssh >/sys/class/leds/\fIledname\fP/trigger diff --git a/extensions/libxt_MARK.c b/extensions/libxt_MARK.c new file mode 100644 index 0000000..dbfc7c0 --- /dev/null +++ b/extensions/libxt_MARK.c @@ -0,0 +1,347 @@ +/* Shared library add-on to iptables to add MARK target support. */ +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_MARK.h> + +/* Version 0 */ +struct xt_mark_target_info { + unsigned long mark; +}; + +/* Version 1 */ +enum { + XT_MARK_SET=0, + XT_MARK_AND, + XT_MARK_OR, +}; + +struct xt_mark_target_info_v1 { + unsigned long mark; + u_int8_t mode; +}; + +enum { + F_MARK = 1 << 0, +}; + +static void MARK_help(void) +{ + printf( +"MARK target options:\n" +" --set-mark value Set nfmark value\n" +" --and-mark value Binary AND the nfmark with value\n" +" --or-mark value Binary OR the nfmark with value\n"); +} + +static const struct option MARK_opts[] = { + { "set-mark", 1, NULL, '1' }, + { "and-mark", 1, NULL, '2' }, + { "or-mark", 1, NULL, '3' }, + { .name = NULL } +}; + +static const struct option mark_tg_opts[] = { + {.name = "set-xmark", .has_arg = true, .val = 'X'}, + {.name = "set-mark", .has_arg = true, .val = '='}, + {.name = "and-mark", .has_arg = true, .val = '&'}, + {.name = "or-mark", .has_arg = true, .val = '|'}, + {.name = "xor-mark", .has_arg = true, .val = '^'}, + { .name = NULL } +}; + +static void mark_tg_help(void) +{ + printf( +"MARK target options:\n" +" --set-xmark value[/mask] Clear bits in mask and XOR value into nfmark\n" +" --set-mark value[/mask] Clear bits in mask and OR value into nfmark\n" +" --and-mark bits Binary AND the nfmark with bits\n" +" --or-mark bits Binary OR the nfmark with bits\n" +" --xor-mask bits Binary XOR the nfmark with bits\n" +"\n"); +} + +/* Function which parses command options; returns true if it + ate an option */ +static int +MARK_parse_v0(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_mark_target_info *markinfo + = (struct xt_mark_target_info *)(*target)->data; + unsigned int mark = 0; + + switch (c) { + case '1': + if (!xtables_strtoui(optarg, NULL, &mark, 0, UINT32_MAX)) + xtables_error(PARAMETER_PROBLEM, "Bad MARK value \"%s\"", optarg); + markinfo->mark = mark; + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "MARK target: Can't specify --set-mark twice"); + *flags = 1; + break; + case '2': + xtables_error(PARAMETER_PROBLEM, + "MARK target: kernel too old for --and-mark"); + case '3': + xtables_error(PARAMETER_PROBLEM, + "MARK target: kernel too old for --or-mark"); + default: + return 0; + } + + return 1; +} + +static void MARK_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "MARK target: Parameter --set/and/or-mark" + " is required"); +} + +static int +MARK_parse_v1(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_mark_target_info_v1 *markinfo + = (struct xt_mark_target_info_v1 *)(*target)->data; + unsigned int mark = 0; + + switch (c) { + case '1': + markinfo->mode = XT_MARK_SET; + break; + case '2': + markinfo->mode = XT_MARK_AND; + break; + case '3': + markinfo->mode = XT_MARK_OR; + break; + default: + return 0; + } + + if (!xtables_strtoui(optarg, NULL, &mark, 0, UINT32_MAX)) + xtables_error(PARAMETER_PROBLEM, "Bad MARK value \"%s\"", optarg); + markinfo->mark = mark; + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "MARK target: Can't specify --set-mark twice"); + + *flags = 1; + return 1; +} + +static int mark_tg_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_mark_tginfo2 *info = (void *)(*target)->data; + unsigned int value, mask = UINT32_MAX; + char *end; + + switch (c) { + case 'X': /* --set-xmark */ + case '=': /* --set-mark */ + xtables_param_act(XTF_ONE_ACTION, "MARK", *flags & F_MARK); + xtables_param_act(XTF_NO_INVERT, "MARK", "--set-xmark/--set-mark", invert); + if (!xtables_strtoui(optarg, &end, &value, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "MARK", "--set-xmark/--set-mark", optarg); + if (*end == '/') + if (!xtables_strtoui(end + 1, &end, &mask, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "MARK", "--set-xmark/--set-mark", optarg); + if (*end != '\0') + xtables_param_act(XTF_BAD_VALUE, "MARK", "--set-xmark/--set-mark", optarg); + info->mark = value; + info->mask = mask; + + if (c == '=') + info->mask = value | mask; + break; + + case '&': /* --and-mark */ + xtables_param_act(XTF_ONE_ACTION, "MARK", *flags & F_MARK); + xtables_param_act(XTF_NO_INVERT, "MARK", "--and-mark", invert); + if (!xtables_strtoui(optarg, NULL, &mask, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "MARK", "--and-mark", optarg); + info->mark = 0; + info->mask = ~mask; + break; + + case '|': /* --or-mark */ + xtables_param_act(XTF_ONE_ACTION, "MARK", *flags & F_MARK); + xtables_param_act(XTF_NO_INVERT, "MARK", "--or-mark", invert); + if (!xtables_strtoui(optarg, NULL, &value, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "MARK", "--or-mark", optarg); + info->mark = value; + info->mask = value; + break; + + case '^': /* --xor-mark */ + xtables_param_act(XTF_ONE_ACTION, "MARK", *flags & F_MARK); + xtables_param_act(XTF_NO_INVERT, "MARK", "--xor-mark", invert); + if (!xtables_strtoui(optarg, NULL, &value, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "MARK", "--xor-mark", optarg); + info->mark = value; + info->mask = 0; + break; + + default: + return false; + } + + *flags |= F_MARK; + return true; +} + +static void mark_tg_check(unsigned int flags) +{ + if (flags == 0) + xtables_error(PARAMETER_PROBLEM, "MARK: One of the --set-xmark, " + "--{and,or,xor,set}-mark options is required"); +} + +static void +print_mark(unsigned long mark) +{ + printf("0x%lx ", mark); +} + +static void MARK_print_v0(const void *ip, + const struct xt_entry_target *target, int numeric) +{ + const struct xt_mark_target_info *markinfo = + (const struct xt_mark_target_info *)target->data; + printf("MARK set "); + print_mark(markinfo->mark); +} + +static void MARK_save_v0(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_mark_target_info *markinfo = + (const struct xt_mark_target_info *)target->data; + + printf("--set-mark "); + print_mark(markinfo->mark); +} + +static void MARK_print_v1(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_mark_target_info_v1 *markinfo = + (const struct xt_mark_target_info_v1 *)target->data; + + switch (markinfo->mode) { + case XT_MARK_SET: + printf("MARK set "); + break; + case XT_MARK_AND: + printf("MARK and "); + break; + case XT_MARK_OR: + printf("MARK or "); + break; + } + print_mark(markinfo->mark); +} + +static void mark_tg_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_mark_tginfo2 *info = (const void *)target->data; + + if (info->mark == 0) + printf("MARK and 0x%x ", (unsigned int)(u_int32_t)~info->mask); + else if (info->mark == info->mask) + printf("MARK or 0x%x ", info->mark); + else if (info->mask == 0) + printf("MARK xor 0x%x ", info->mark); + else if (info->mask == 0xffffffffU) + printf("MARK set 0x%x ", info->mark); + else + printf("MARK xset 0x%x/0x%x ", info->mark, info->mask); +} + +static void MARK_save_v1(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_mark_target_info_v1 *markinfo = + (const struct xt_mark_target_info_v1 *)target->data; + + switch (markinfo->mode) { + case XT_MARK_SET: + printf("--set-mark "); + break; + case XT_MARK_AND: + printf("--and-mark "); + break; + case XT_MARK_OR: + printf("--or-mark "); + break; + } + print_mark(markinfo->mark); +} + +static void mark_tg_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_mark_tginfo2 *info = (const void *)target->data; + + printf("--set-xmark 0x%x/0x%x ", info->mark, info->mask); +} + +static struct xtables_target mark_tg_reg[] = { + { + .family = NFPROTO_UNSPEC, + .name = "MARK", + .version = XTABLES_VERSION, + .revision = 0, + .size = XT_ALIGN(sizeof(struct xt_mark_target_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_mark_target_info)), + .help = MARK_help, + .parse = MARK_parse_v0, + .final_check = MARK_check, + .print = MARK_print_v0, + .save = MARK_save_v0, + .extra_opts = MARK_opts, + }, + { + .family = NFPROTO_IPV4, + .name = "MARK", + .version = XTABLES_VERSION, + .revision = 1, + .size = XT_ALIGN(sizeof(struct xt_mark_target_info_v1)), + .userspacesize = XT_ALIGN(sizeof(struct xt_mark_target_info_v1)), + .help = MARK_help, + .parse = MARK_parse_v1, + .final_check = MARK_check, + .print = MARK_print_v1, + .save = MARK_save_v1, + .extra_opts = MARK_opts, + }, + { + .version = XTABLES_VERSION, + .name = "MARK", + .revision = 2, + .family = NFPROTO_UNSPEC, + .size = XT_ALIGN(sizeof(struct xt_mark_tginfo2)), + .userspacesize = XT_ALIGN(sizeof(struct xt_mark_tginfo2)), + .help = mark_tg_help, + .parse = mark_tg_parse, + .final_check = mark_tg_check, + .print = mark_tg_print, + .save = mark_tg_save, + .extra_opts = mark_tg_opts, + }, +}; + +void _init(void) +{ + xtables_register_targets(mark_tg_reg, ARRAY_SIZE(mark_tg_reg)); +} diff --git a/extensions/libxt_MARK.man b/extensions/libxt_MARK.man new file mode 100644 index 0000000..aaeceb4 --- /dev/null +++ b/extensions/libxt_MARK.man @@ -0,0 +1,27 @@ +This target is used to set the Netfilter mark value associated with the packet. +It can, for example, be used in conjunction with routing based on fwmark (needs +iproute2). If you plan on doing so, note that the mark needs to be set in the +PREROUTING chain of the mangle table to affect routing. +The mark field is 32 bits wide. +.TP +\fB\-\-set\-xmark\fP \fIvalue\fP[\fB/\fP\fImask\fP] +Zeroes out the bits given by \fImask\fR and XORs \fIvalue\fR into the packet +mark ("nfmark"). If \fImask\fR is omitted, 0xFFFFFFFF is assumed. +.TP +\fB\-\-set\-mark\fP \fIvalue\fP[\fB/\fP\fImask\fP] +Zeroes out the bits given by \fImask\fR and ORs \fIvalue\fR into the packet +mark. If \fImask\fR is omitted, 0xFFFFFFFF is assumed. +.PP +The following mnemonics are available: +.TP +\fB\-\-and\-mark\fP \fIbits\fP +Binary AND the nfmark with \fIbits\fR. (Mnemonic for \fB\-\-set\-xmark +0/\fR\fIinvbits\fR, where \fIinvbits\fR is the binary negation of \fIbits\fR.) +.TP +\fB\-\-or\-mark\fP \fIbits\fP +Binary OR the nfmark with \fIbits\fR. (Mnemonic for \fB\-\-set\-xmark\fP +\fIbits\fR\fB/\fR\fIbits\fR.) +.TP +\fB\-\-xor\-mark\fP \fIbits\fP +Binary XOR the nfmark with \fIbits\fR. (Mnemonic for \fB\-\-set\-xmark\fP +\fIbits\fR\fB/0\fR.) diff --git a/extensions/libxt_NFLOG.c b/extensions/libxt_NFLOG.c new file mode 100644 index 0000000..e2185d5 --- /dev/null +++ b/extensions/libxt_NFLOG.c @@ -0,0 +1,158 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <getopt.h> +#include <xtables.h> + +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_NFLOG.h> + +enum { + NFLOG_GROUP = 0x1, + NFLOG_PREFIX = 0x2, + NFLOG_RANGE = 0x4, + NFLOG_THRESHOLD = 0x8, +}; + +static const struct option NFLOG_opts[] = { + { "nflog-group", 1, NULL, NFLOG_GROUP }, + { "nflog-prefix", 1, NULL, NFLOG_PREFIX }, + { "nflog-range", 1, NULL, NFLOG_RANGE }, + { "nflog-threshold", 1, NULL, NFLOG_THRESHOLD }, + { .name = NULL } +}; + +static void NFLOG_help(void) +{ + printf("NFLOG target options:\n" + " --nflog-group NUM NETLINK group used for logging\n" + " --nflog-range NUM Number of byte to copy\n" + " --nflog-threshold NUM Message threshold of in-kernel queue\n" + " --nflog-prefix STRING Prefix string for log messages\n"); +} + +static void NFLOG_init(struct xt_entry_target *t) +{ + struct xt_nflog_info *info = (struct xt_nflog_info *)t->data; + + info->group = 0; + info->threshold = XT_NFLOG_DEFAULT_THRESHOLD; +} + +static int NFLOG_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_nflog_info *info = (struct xt_nflog_info *)(*target)->data; + int n; + size_t length; + + switch (c) { + case NFLOG_GROUP: + if (*flags & NFLOG_GROUP) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --nflog-group twice"); + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --nflog-group"); + + n = atoi(optarg); + if (n < 0) + xtables_error(PARAMETER_PROBLEM, + "--nflog-group can not be negative"); + info->group = n; + break; + case NFLOG_PREFIX: + if (*flags & NFLOG_PREFIX) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --nflog-prefix twice"); + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --nflog-prefix"); + + length = strlen(optarg); + if (length == 0) + xtables_error(PARAMETER_PROBLEM, + "No prefix specified for --nflog-prefix"); + if (length >= sizeof(info->prefix)) + xtables_error(PARAMETER_PROBLEM, + "--nflog-prefix too long, max %Zu characters", + sizeof(info->prefix) - 1); + if (length != strlen(strtok(optarg, "\n"))) + xtables_error(PARAMETER_PROBLEM, + "Newlines are not allowed in --nflog-prefix"); + strcpy(info->prefix, optarg); + break; + case NFLOG_RANGE: + if (*flags & NFLOG_RANGE) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --nflog-range twice"); + n = atoi(optarg); + if (n < 0) + xtables_error(PARAMETER_PROBLEM, + "Invalid --nflog-range, must be >= 0"); + info->len = n; + break; + case NFLOG_THRESHOLD: + if (*flags & NFLOG_THRESHOLD) + xtables_error(PARAMETER_PROBLEM, + "Can't specify --nflog-threshold twice"); + n = atoi(optarg); + if (n < 1) + xtables_error(PARAMETER_PROBLEM, + "Invalid --nflog-threshold, must be >= 1"); + info->threshold = n; + break; + default: + return 0; + } + *flags |= c; + return 1; +} + +static void nflog_print(const struct xt_nflog_info *info, char *prefix) +{ + if (info->prefix[0] != '\0') { + printf("%snflog-prefix ", prefix); + xtables_save_string(info->prefix); + } + if (info->group) + printf("%snflog-group %u ", prefix, info->group); + if (info->len) + printf("%snflog-range %u ", prefix, info->len); + if (info->threshold != XT_NFLOG_DEFAULT_THRESHOLD) + printf("%snflog-threshold %u ", prefix, info->threshold); +} + +static void NFLOG_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_nflog_info *info = (struct xt_nflog_info *)target->data; + + nflog_print(info, ""); +} + +static void NFLOG_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_nflog_info *info = (struct xt_nflog_info *)target->data; + + nflog_print(info, "--"); +} + +static struct xtables_target nflog_target = { + .family = NFPROTO_UNSPEC, + .name = "NFLOG", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_nflog_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_nflog_info)), + .help = NFLOG_help, + .init = NFLOG_init, + .parse = NFLOG_parse, + .print = NFLOG_print, + .save = NFLOG_save, + .extra_opts = NFLOG_opts, +}; + +void _init(void) +{ + xtables_register_target(&nflog_target); +} diff --git a/extensions/libxt_NFLOG.man b/extensions/libxt_NFLOG.man new file mode 100644 index 0000000..66f0b97 --- /dev/null +++ b/extensions/libxt_NFLOG.man @@ -0,0 +1,29 @@ +This target provides logging of matching packets. When this target is +set for a rule, the Linux kernel will pass the packet to the loaded +logging backend to log the packet. This is usually used in combination +with nfnetlink_log as logging backend, which will multicast the packet +through a +.IR netlink +socket to the specified multicast group. One or more userspace processes +may subscribe to the group to receive the packets. Like LOG, this is a +non-terminating target, i.e. rule traversal continues at the next rule. +.TP +\fB\-\-nflog\-group\fP \fInlgroup\fP +The netlink group (1 \- 2^32\-1) to which packets are (only applicable for +nfnetlink_log). The default value is 0. +.TP +\fB\-\-nflog\-prefix\fP \fIprefix\fP +A prefix string to include in the log message, up to 64 characters +long, useful for distinguishing messages in the logs. +.TP +\fB\-\-nflog\-range\fP \fIsize\fP +The number of bytes to be copied to userspace (only applicable for +nfnetlink_log). nfnetlink_log instances may specify their own +range, this option overrides it. +.TP +\fB\-\-nflog\-threshold\fP \fIsize\fP +Number of packets to queue inside the kernel before sending them +to userspace (only applicable for nfnetlink_log). Higher values +result in less overhead per packet, but increase delay until the +packets reach userspace. The default value is 1. +.BR diff --git a/extensions/libxt_NFQUEUE.c b/extensions/libxt_NFQUEUE.c new file mode 100644 index 0000000..2d9d98a --- /dev/null +++ b/extensions/libxt_NFQUEUE.c @@ -0,0 +1,204 @@ +/* Shared library add-on to iptables for NFQ + * + * (C) 2005 by Harald Welte <laforge@netfilter.org> + * + * This program is distributed under the terms of GNU GPL v2, 1991 + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_NFQUEUE.h> + +static void NFQUEUE_help(void) +{ + printf( +"NFQUEUE target options\n" +" --queue-num value Send packet to QUEUE number <value>.\n" +" Valid queue numbers are 0-65535\n" +); +} + +static void NFQUEUE_help_v1(void) +{ + NFQUEUE_help(); + printf( +" --queue-balance first:last Balance flows between queues <value> to <value>.\n"); +} + +static const struct option NFQUEUE_opts[] = { + { "queue-num", 1, NULL, 'F' }, + { "queue-balance", 1, NULL, 'B' }, + { .name = NULL } +}; + +static void exit_badqueue(const char *s) +{ + xtables_error(PARAMETER_PROBLEM, "Invalid queue number `%s'\n", s); +} + +static void +parse_num(const char *s, struct xt_NFQ_info *tinfo) +{ + unsigned int num; + + if (!xtables_strtoui(s, NULL, &num, 0, UINT16_MAX)) + exit_badqueue(s); + + tinfo->queuenum = num; +} + +static int +NFQUEUE_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_NFQ_info *tinfo + = (struct xt_NFQ_info *)(*target)->data; + + switch (c) { + case 'F': + if (*flags) + xtables_error(PARAMETER_PROBLEM, "NFQUEUE target: " + "Only use --queue-num ONCE!"); + parse_num(optarg, tinfo); + break; + case 'B': + xtables_error(PARAMETER_PROBLEM, "NFQUEUE target: " + "--queue-balance not supported (kernel too old?)"); + default: + return 0; + } + + return 1; +} + +static int +NFQUEUE_parse_v1(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_NFQ_info_v1 *info = (void *)(*target)->data; + char *colon; + unsigned int firstqueue, lastqueue; + + switch (c) { + case 'F': /* fallthrough */ + case 'B': + if (*flags) + xtables_error(PARAMETER_PROBLEM, "NFQUEUE target: " + "Only use --queue-num ONCE!"); + + if (!xtables_strtoui(optarg, &colon, &firstqueue, 0, UINT16_MAX)) + exit_badqueue(optarg); + + info->queuenum = firstqueue; + + if (c == 'F') { + if (*colon) + exit_badqueue(optarg); + break; + } + + if (*colon != ':') + xtables_error(PARAMETER_PROBLEM, "Bad range \"%s\"", optarg); + + if (!xtables_strtoui(colon + 1, NULL, &lastqueue, 1, UINT16_MAX)) + exit_badqueue(optarg); + + if (firstqueue >= lastqueue) + xtables_error(PARAMETER_PROBLEM, "%u should be less than %u", + firstqueue, lastqueue); + info->queues_total = lastqueue - firstqueue + 1; + break; + default: + return 0; + } + + return 1; +} + +static void NFQUEUE_print(const void *ip, + const struct xt_entry_target *target, int numeric) +{ + const struct xt_NFQ_info *tinfo = + (const struct xt_NFQ_info *)target->data; + printf("NFQUEUE num %u", tinfo->queuenum); +} + +static void NFQUEUE_print_v1(const void *ip, + const struct xt_entry_target *target, int numeric) +{ + const struct xt_NFQ_info_v1 *tinfo = (const void *)target->data; + unsigned int last = tinfo->queues_total; + + if (last > 1) { + last += tinfo->queuenum - 1; + printf("NFQUEUE balance %u:%u", tinfo->queuenum, last); + } else { + printf("NFQUEUE num %u", tinfo->queuenum); + } +} + +static void NFQUEUE_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_NFQ_info *tinfo = + (const struct xt_NFQ_info *)target->data; + + printf("--queue-num %u ", tinfo->queuenum); +} + +static void NFQUEUE_save_v1(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_NFQ_info_v1 *tinfo = (const void *)target->data; + unsigned int last = tinfo->queues_total; + + if (last > 1) { + last += tinfo->queuenum - 1; + printf("--queue-balance %u:%u ", tinfo->queuenum, last); + } else { + printf("--queue-num %u ", tinfo->queuenum); + } +} + +static void NFQUEUE_init_v1(struct xt_entry_target *t) +{ + struct xt_NFQ_info_v1 *tinfo = (void *)t->data; + tinfo->queues_total = 1; +} + +static struct xtables_target nfqueue_target = { + .family = NFPROTO_UNSPEC, + .name = "NFQUEUE", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_NFQ_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_NFQ_info)), + .help = NFQUEUE_help, + .parse = NFQUEUE_parse, + .print = NFQUEUE_print, + .save = NFQUEUE_save, + .extra_opts = NFQUEUE_opts +}; + +static struct xtables_target nfqueue_target_v1 = { + .family = NFPROTO_UNSPEC, + .revision = 1, + .name = "NFQUEUE", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_NFQ_info_v1)), + .userspacesize = XT_ALIGN(sizeof(struct xt_NFQ_info_v1)), + .help = NFQUEUE_help_v1, + .init = NFQUEUE_init_v1, + .parse = NFQUEUE_parse_v1, + .print = NFQUEUE_print_v1, + .save = NFQUEUE_save_v1, + .extra_opts = NFQUEUE_opts, +}; + +void _init(void) +{ + xtables_register_target(&nfqueue_target); + xtables_register_target(&nfqueue_target_v1); +} diff --git a/extensions/libxt_NFQUEUE.man b/extensions/libxt_NFQUEUE.man new file mode 100644 index 0000000..59eddfc --- /dev/null +++ b/extensions/libxt_NFQUEUE.man @@ -0,0 +1,18 @@ +This target is an extension of the QUEUE target. As opposed to QUEUE, it allows +you to put a packet into any specific queue, identified by its 16-bit queue +number. +It can only be used with Kernel versions 2.6.14 or later, since it requires +the +.B +nfnetlink_queue +kernel support. The \fBqueue-balance\fP option was added in Linux 2.6.31. +.TP +\fB\-\-queue\-num\fP \fIvalue\fP +This specifies the QUEUE number to use. Valid queue numbers are 0 to 65535. The default value is 0. +.PP +.TP +\fB\-\-queue\-balance\fP \fIvalue\fP\fB:\fP\fIvalue\fP +This specifies a range of queues to use. Packets are then balanced across the given queues. +This is useful for multicore systems: start multiple instances of the userspace program on +queues x, x+1, .. x+n and use "\-\-queue\-balance \fIx\fP\fB:\fP\fIx+n\fP". +Packets belonging to the same connection are put into the same nfqueue. diff --git a/extensions/libxt_NOTRACK.c b/extensions/libxt_NOTRACK.c new file mode 100644 index 0000000..ca58700 --- /dev/null +++ b/extensions/libxt_NOTRACK.c @@ -0,0 +1,15 @@ +/* Shared library add-on to iptables to add NOTRACK target support. */ +#include <xtables.h> + +static struct xtables_target notrack_target = { + .family = NFPROTO_UNSPEC, + .name = "NOTRACK", + .version = XTABLES_VERSION, + .size = XT_ALIGN(0), + .userspacesize = XT_ALIGN(0), +}; + +void _init(void) +{ + xtables_register_target(¬rack_target); +} diff --git a/extensions/libxt_NOTRACK.man b/extensions/libxt_NOTRACK.man new file mode 100644 index 0000000..c2cdf5a --- /dev/null +++ b/extensions/libxt_NOTRACK.man @@ -0,0 +1,5 @@ +This target disables connection tracking for all packets matching that rule. +.PP +It can only be used in the +.B raw +table. diff --git a/extensions/libxt_RATEEST.c b/extensions/libxt_RATEEST.c new file mode 100644 index 0000000..4b7831f --- /dev/null +++ b/extensions/libxt_RATEEST.c @@ -0,0 +1,222 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <getopt.h> +#include <math.h> + +#include <xtables.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_RATEEST.h> + +/* hack to pass raw values to final_check */ +static struct xt_rateest_target_info *RATEEST_info; +static unsigned int interval; +static unsigned int ewma_log; + +static void +RATEEST_help(void) +{ + printf( +"RATEEST target options:\n" +" --rateest-name name Rate estimator name\n" +" --rateest-interval sec Rate measurement interval in seconds\n" +" --rateest-ewmalog value Rate measurement averaging time constant\n"); +} + +enum RATEEST_options { + RATEEST_OPT_NAME, + RATEEST_OPT_INTERVAL, + RATEEST_OPT_EWMALOG, +}; + +static const struct option RATEEST_opts[] = { + { "rateest-name", 1, NULL, RATEEST_OPT_NAME }, + { "rateest-interval", 1, NULL, RATEEST_OPT_INTERVAL }, + { "rateest-ewmalog", 1, NULL, RATEEST_OPT_EWMALOG }, + { .name = NULL }, +}; + +/* Copied from iproute */ +#define TIME_UNITS_PER_SEC 1000000 + +static int +RATEEST_get_time(unsigned int *time, const char *str) +{ + double t; + char *p; + + t = strtod(str, &p); + if (p == str) + return -1; + + if (*p) { + if (strcasecmp(p, "s") == 0 || strcasecmp(p, "sec")==0 || + strcasecmp(p, "secs")==0) + t *= TIME_UNITS_PER_SEC; + else if (strcasecmp(p, "ms") == 0 || strcasecmp(p, "msec")==0 || + strcasecmp(p, "msecs") == 0) + t *= TIME_UNITS_PER_SEC/1000; + else if (strcasecmp(p, "us") == 0 || strcasecmp(p, "usec")==0 || + strcasecmp(p, "usecs") == 0) + t *= TIME_UNITS_PER_SEC/1000000; + else + return -1; + } + + *time = t; + return 0; +} + +static void +RATEEST_print_time(unsigned int time) +{ + double tmp = time; + + if (tmp >= TIME_UNITS_PER_SEC) + printf("%.1fs ", tmp/TIME_UNITS_PER_SEC); + else if (tmp >= TIME_UNITS_PER_SEC/1000) + printf("%.1fms ", tmp/(TIME_UNITS_PER_SEC/1000)); + else + printf("%uus ", time); +} + +static void +RATEEST_init(struct xt_entry_target *target) +{ + interval = 0; + ewma_log = 0; +} + +static int +RATEEST_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_rateest_target_info *info = (void *)(*target)->data; + + RATEEST_info = info; + + switch (c) { + case RATEEST_OPT_NAME: + if (*flags & (1 << c)) + xtables_error(PARAMETER_PROBLEM, + "RATEEST: can't specify --rateest-name twice"); + *flags |= 1 << c; + + strncpy(info->name, optarg, sizeof(info->name) - 1); + break; + + case RATEEST_OPT_INTERVAL: + if (*flags & (1 << c)) + xtables_error(PARAMETER_PROBLEM, + "RATEEST: can't specify --rateest-interval twice"); + *flags |= 1 << c; + + if (RATEEST_get_time(&interval, optarg) < 0) + xtables_error(PARAMETER_PROBLEM, + "RATEEST: bad interval value `%s'", optarg); + + break; + + case RATEEST_OPT_EWMALOG: + if (*flags & (1 << c)) + xtables_error(PARAMETER_PROBLEM, + "RATEEST: can't specify --rateest-ewmalog twice"); + *flags |= 1 << c; + + if (RATEEST_get_time(&ewma_log, optarg) < 0) + xtables_error(PARAMETER_PROBLEM, + "RATEEST: bad ewmalog value `%s'", optarg); + + break; + + default: + return 0; + } + + return 1; +} + +static void +RATEEST_final_check(unsigned int flags) +{ + struct xt_rateest_target_info *info = RATEEST_info; + + if (!(flags & (1 << RATEEST_OPT_NAME))) + xtables_error(PARAMETER_PROBLEM, "RATEEST: no name specified"); + if (!(flags & (1 << RATEEST_OPT_INTERVAL))) + xtables_error(PARAMETER_PROBLEM, "RATEEST: no interval specified"); + if (!(flags & (1 << RATEEST_OPT_EWMALOG))) + xtables_error(PARAMETER_PROBLEM, "RATEEST: no ewmalog specified"); + + for (info->interval = 0; info->interval <= 5; info->interval++) { + if (interval <= (1 << info->interval) * (TIME_UNITS_PER_SEC / 4)) + break; + } + + if (info->interval > 5) + xtables_error(PARAMETER_PROBLEM, + "RATEEST: interval value is too large"); + info->interval -= 2; + + for (info->ewma_log = 1; info->ewma_log < 32; info->ewma_log++) { + double w = 1.0 - 1.0 / (1 << info->ewma_log); + if (interval / (-log(w)) > ewma_log) + break; + } + info->ewma_log--; + + if (info->ewma_log == 0 || info->ewma_log >= 31) + xtables_error(PARAMETER_PROBLEM, + "RATEEST: ewmalog value is out of range"); +} + +static void +__RATEEST_print(const struct xt_entry_target *target, const char *prefix) +{ + const struct xt_rateest_target_info *info = (const void *)target->data; + unsigned int local_interval; + unsigned int local_ewma_log; + + local_interval = (TIME_UNITS_PER_SEC << (info->interval + 2)) / 4; + local_ewma_log = local_interval * (1 << (info->ewma_log)); + + printf("%sname %s ", prefix, info->name); + printf("%sinterval ", prefix); + RATEEST_print_time(local_interval); + printf("%sewmalog ", prefix); + RATEEST_print_time(local_ewma_log); +} + +static void +RATEEST_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + __RATEEST_print(target, ""); +} + +static void +RATEEST_save(const void *ip, const struct xt_entry_target *target) +{ + __RATEEST_print(target, "--rateest-"); +} + +static struct xtables_target rateest_tg_reg = { + .family = NFPROTO_UNSPEC, + .name = "RATEEST", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_rateest_target_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_rateest_target_info)), + .help = RATEEST_help, + .init = RATEEST_init, + .parse = RATEEST_parse, + .final_check = RATEEST_final_check, + .print = RATEEST_print, + .save = RATEEST_save, + .extra_opts = RATEEST_opts, +}; + +void _init(void) +{ + xtables_register_target(&rateest_tg_reg); +} diff --git a/extensions/libxt_RATEEST.man b/extensions/libxt_RATEEST.man new file mode 100644 index 0000000..37de759 --- /dev/null +++ b/extensions/libxt_RATEEST.man @@ -0,0 +1,12 @@ +The RATEEST target collects statistics, performs rate estimation calculation +and saves the results for later evaluation using the \fBrateest\fP match. +.TP +\fB\-\-rateest\-name\fP \fIname\fP +Count matched packets into the pool referred to by \fIname\fP, which is freely +choosable. +.TP +\fB\-\-rateest\-interval\fP \fIamount\fP{\fBs\fP|\fBms\fP|\fBus\fP} +Rate measurement interval, in seconds, milliseconds or microseconds. +.TP +\fB\-\-rateest\-ewmalog\fP \fIvalue\fP +Rate measurement averaging time constant. diff --git a/extensions/libxt_SECMARK.c b/extensions/libxt_SECMARK.c new file mode 100644 index 0000000..2152b6f --- /dev/null +++ b/extensions/libxt_SECMARK.c @@ -0,0 +1,113 @@ +/* + * Shared library add-on to iptables to add SECMARK target support. + * + * Based on the MARK target. + * + * Copyright (C) 2006 Red Hat, Inc., James Morris <jmorris@redhat.com> + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <linux/netfilter/xt_SECMARK.h> + +#define PFX "SECMARK target: " + +static void SECMARK_help(void) +{ + printf( +"SECMARK target options:\n" +" --selctx value Set the SELinux security context\n"); +} + +static const struct option SECMARK_opts[] = { + { "selctx", 1, NULL, '1' }, + { .name = NULL } +}; + +static int SECMARK_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_secmark_target_info *info = + (struct xt_secmark_target_info*)(*target)->data; + + switch (c) { + case '1': + if (*flags & SECMARK_MODE_SEL) + xtables_error(PARAMETER_PROBLEM, PFX + "Can't specify --selctx twice"); + info->mode = SECMARK_MODE_SEL; + + if (strlen(optarg) > SECMARK_SELCTX_MAX-1) + xtables_error(PARAMETER_PROBLEM, PFX + "Maximum length %u exceeded by --selctx" + " parameter (%zu)", + SECMARK_SELCTX_MAX-1, strlen(optarg)); + + strcpy(info->u.sel.selctx, optarg); + *flags |= SECMARK_MODE_SEL; + break; + default: + return 0; + } + + return 1; +} + +static void SECMARK_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, PFX "parameter required"); +} + +static void print_secmark(const struct xt_secmark_target_info *info) +{ + switch (info->mode) { + case SECMARK_MODE_SEL: + printf("selctx %s ", info->u.sel.selctx);\ + break; + + default: + xtables_error(OTHER_PROBLEM, PFX "invalid mode %hhu\n", info->mode); + } +} + +static void SECMARK_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_secmark_target_info *info = + (struct xt_secmark_target_info*)(target)->data; + + printf("SECMARK "); + print_secmark(info); +} + +static void SECMARK_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_secmark_target_info *info = + (struct xt_secmark_target_info*)target->data; + + printf("--"); + print_secmark(info); +} + +static struct xtables_target secmark_target = { + .family = NFPROTO_UNSPEC, + .name = "SECMARK", + .version = XTABLES_VERSION, + .revision = 0, + .size = XT_ALIGN(sizeof(struct xt_secmark_target_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_secmark_target_info)), + .help = SECMARK_help, + .parse = SECMARK_parse, + .final_check = SECMARK_check, + .print = SECMARK_print, + .save = SECMARK_save, + .extra_opts = SECMARK_opts, +}; + +void _init(void) +{ + xtables_register_target(&secmark_target); +} diff --git a/extensions/libxt_SECMARK.man b/extensions/libxt_SECMARK.man new file mode 100644 index 0000000..e44efce --- /dev/null +++ b/extensions/libxt_SECMARK.man @@ -0,0 +1,7 @@ +This is used to set the security mark value associated with the +packet for use by security subsystems such as SELinux. It is only +valid in the +.B mangle +table. The mark is 32 bits wide. +.TP +\fB\-\-selctx\fP \fIsecurity_context\fP diff --git a/extensions/libxt_SET.c b/extensions/libxt_SET.c new file mode 100644 index 0000000..f6386a9 --- /dev/null +++ b/extensions/libxt_SET.c @@ -0,0 +1,286 @@ +/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> + * Patrick Schaaf <bof@bof.de> + * Martin Josefsson <gandalf@wlug.westbo.se> + * Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Shared library add-on to iptables to add IP set mangling target. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <ctype.h> + +#include <xtables.h> +#include <linux/netfilter/xt_set.h> +#include "libxt_set.h" + +static void +set_target_help(void) +{ + printf("SET target options:\n" + " --add-set name flags\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[] = { + { .name = "add-set", .has_arg = true, .val = '1'}, + { .name = "del-set", .has_arg = true, .val = '2'}, + { .name = NULL } +}; + +static void +set_target_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "You must specify either `--add-set' or `--del-set'"); +} + +static void +set_target_init_v0(struct xt_entry_target *target) +{ + struct xt_set_info_target_v0 *info = + (struct xt_set_info_target_v0 *) target->data; + + info->add_set.index = + info->del_set.index = IPSET_INVALID_ID; + +} + +static void +parse_target_v0(char **argv, int invert, unsigned int *flags, + struct xt_set_info_v0 *info, const char *what) +{ + if (info->u.flags[0]) + xtables_error(PARAMETER_PROBLEM, + "--%s can be specified only once", what); + + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --%s", what); + + if (!argv[optind] + || argv[optind][0] == '-' || argv[optind][0] == '!') + xtables_error(PARAMETER_PROBLEM, + "--%s requires two args.", what); + + if (strlen(optarg) > IPSET_MAXNAMELEN - 1) + xtables_error(PARAMETER_PROBLEM, + "setname `%s' too long, max %d characters.", + optarg, IPSET_MAXNAMELEN - 1); + + get_set_byname(optarg, (struct xt_set_info *)info); + parse_dirs_v0(argv[optind], info); + optind++; + + *flags = 1; +} + +static int +set_target_parse_v0(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_set_info_target_v0 *myinfo = + (struct xt_set_info_target_v0 *) (*target)->data; + + switch (c) { + case '1': /* --add-set <set> <flags> */ + parse_target_v0(argv, invert, flags, + &myinfo->add_set, "add-set"); + break; + case '2': /* --del-set <set>[:<flags>] <flags> */ + parse_target_v0(argv, invert, flags, + &myinfo->del_set, "del-set"); + break; + + default: + return 0; + } + return 1; +} + +static void +print_target_v0(const char *prefix, const struct xt_set_info_v0 *info) +{ + int i; + char setname[IPSET_MAXNAMELEN]; + + if (info->index == IPSET_INVALID_ID) + return; + get_set_byid(setname, info->index); + printf("%s %s", prefix, setname); + for (i = 0; i < IPSET_DIM_MAX; i++) { + if (!info->u.flags[i]) + break; + printf("%s%s", + i == 0 ? " " : ",", + info->u.flags[i] & IPSET_SRC ? "src" : "dst"); + } + printf(" "); +} + +static void +set_target_print_v0(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_set_info_target_v0 *info = (const void *)target->data; + + print_target_v0("add-set", &info->add_set); + print_target_v0("del-set", &info->del_set); +} + +static void +set_target_save_v0(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_set_info_target_v0 *info = (const void *)target->data; + + print_target_v0("--add-set", &info->add_set); + print_target_v0("--del-set", &info->del_set); +} + +static void +set_target_init(struct xt_entry_target *target) +{ + struct xt_set_info_target *info = + (struct xt_set_info_target *) target->data; + + info->add_set.index = + info->del_set.index = IPSET_INVALID_ID; + +} + +static void +parse_target(char **argv, int invert, unsigned int *flags, + struct xt_set_info *info, const char *what) +{ + if (info->dim) + xtables_error(PARAMETER_PROBLEM, + "--%s can be specified only once", what); + + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, + "Unexpected `!' after --%s", what); + + if (!argv[optind] + || argv[optind][0] == '-' || argv[optind][0] == '!') + xtables_error(PARAMETER_PROBLEM, + "--%s requires two args.", what); + + if (strlen(optarg) > IPSET_MAXNAMELEN - 1) + xtables_error(PARAMETER_PROBLEM, + "setname `%s' too long, max %d characters.", + optarg, IPSET_MAXNAMELEN - 1); + + 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) +{ + struct xt_set_info_target *myinfo = + (struct xt_set_info_target *) (*target)->data; + + switch (c) { + case '1': /* --add-set <set> <flags> */ + parse_target(argv, invert, flags, + &myinfo->add_set, "add-set"); + break; + case '2': /* --del-set <set>[:<flags>] <flags> */ + parse_target(argv, invert, flags, + &myinfo->del_set, "del-set"); + break; + + default: + return 0; + } + return 1; +} + +static void +print_target(const char *prefix, const struct xt_set_info *info) +{ + int i; + char setname[IPSET_MAXNAMELEN]; + + if (info->index == IPSET_INVALID_ID) + return; + get_set_byid(setname, info->index); + printf("%s %s", prefix, setname); + for (i = 1; i <= IPSET_DIM_MAX; i++) { + printf("%s%s", + i == 1 ? " " : ",", + info->flags & (1 << i) ? "src" : "dst"); + } + printf(" "); +} + +static void +set_target_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_set_info_target *info = (const void *)target->data; + + print_target("add-set", &info->add_set); + print_target("del-set", &info->del_set); +} + +static void +set_target_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_set_info_target *info = (const void *)target->data; + + print_target("--add-set", &info->add_set); + print_target("--del-set", &info->del_set); +} + +static struct xtables_target set_tg_reg[] = { + { + .name = "SET", + .revision = 0, + .version = XTABLES_VERSION, + .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, + .init = set_target_init_v0, + .parse = set_target_parse_v0, + .final_check = set_target_check, + .print = set_target_print_v0, + .save = set_target_save_v0, + .extra_opts = set_target_opts, + }, + { + .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, + }, +}; + +void _init(void) +{ + xtables_register_targets(set_tg_reg, ARRAY_SIZE(set_tg_reg)); +} diff --git a/extensions/libxt_SET.man b/extensions/libxt_SET.man new file mode 100644 index 0000000..ea80c2a --- /dev/null +++ b/extensions/libxt_SET.man @@ -0,0 +1,18 @@ +This modules adds and/or deletes entries from IP sets which can be defined +by ipset(8). +.TP +\fB\-\-add\-set\fP \fIsetname\fP \fIflag\fP[\fB,\fP\fIflag\fP...] +add the address(es)/port(s) of the packet to the sets +.TP +\fB\-\-del\-set\fP \fIsetname\fP \fIflag\fP[\fB,\fP\fIflag\fP...] +delete the address(es)/port(s) of the packet from the sets +.IP +where flags are +.BR "src" +and/or +.BR "dst" +specifications and there can be no more than six of them. +.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 +to be installed. diff --git a/extensions/libxt_TCPMSS.c b/extensions/libxt_TCPMSS.c new file mode 100644 index 0000000..ac9e2d0 --- /dev/null +++ b/extensions/libxt_TCPMSS.c @@ -0,0 +1,154 @@ +/* Shared library add-on to iptables to add TCPMSS target support. + * + * Copyright (c) 2000 Marc Boucher +*/ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_TCPMSS.h> + +struct mssinfo { + struct xt_entry_target t; + struct xt_tcpmss_info mss; +}; + +static void __TCPMSS_help(int hdrsize) +{ + printf( +"TCPMSS target mutually-exclusive options:\n" +" --set-mss value explicitly set MSS option to specified value\n" +" --clamp-mss-to-pmtu automatically clamp MSS value to (path_MTU - %d)\n", +hdrsize); +} + +static void TCPMSS_help(void) +{ + __TCPMSS_help(40); +} + +static void TCPMSS_help6(void) +{ + __TCPMSS_help(60); +} + +static const struct option TCPMSS_opts[] = { + { "set-mss", 1, NULL, '1' }, + { "clamp-mss-to-pmtu", 0, NULL, '2' }, + { .name = NULL } +}; + +static int __TCPMSS_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target, + int hdrsize) +{ + struct xt_tcpmss_info *mssinfo + = (struct xt_tcpmss_info *)(*target)->data; + + switch (c) { + unsigned int mssval; + + case '1': + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "TCPMSS target: Only one option may be specified"); + if (!xtables_strtoui(optarg, NULL, &mssval, + 0, UINT16_MAX - hdrsize)) + xtables_error(PARAMETER_PROBLEM, "Bad TCPMSS value \"%s\"", optarg); + + mssinfo->mss = mssval; + *flags = 1; + break; + + case '2': + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "TCPMSS target: Only one option may be specified"); + mssinfo->mss = XT_TCPMSS_CLAMP_PMTU; + *flags = 1; + break; + + default: + return 0; + } + + return 1; +} + +static int TCPMSS_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + return __TCPMSS_parse(c, argv, invert, flags, entry, target, 40); +} + +static int TCPMSS_parse6(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + return __TCPMSS_parse(c, argv, invert, flags, entry, target, 60); +} + +static void TCPMSS_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "TCPMSS target: At least one parameter is required"); +} + +static void TCPMSS_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_tcpmss_info *mssinfo = + (const struct xt_tcpmss_info *)target->data; + if(mssinfo->mss == XT_TCPMSS_CLAMP_PMTU) + printf("TCPMSS clamp to PMTU "); + else + printf("TCPMSS set %u ", mssinfo->mss); +} + +static void TCPMSS_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_tcpmss_info *mssinfo = + (const struct xt_tcpmss_info *)target->data; + + if(mssinfo->mss == XT_TCPMSS_CLAMP_PMTU) + printf("--clamp-mss-to-pmtu "); + else + printf("--set-mss %u ", mssinfo->mss); +} + +static struct xtables_target tcpmss_target = { + .family = NFPROTO_IPV4, + .name = "TCPMSS", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_tcpmss_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_tcpmss_info)), + .help = TCPMSS_help, + .parse = TCPMSS_parse, + .final_check = TCPMSS_check, + .print = TCPMSS_print, + .save = TCPMSS_save, + .extra_opts = TCPMSS_opts, +}; + +static struct xtables_target tcpmss_target6 = { + .family = NFPROTO_IPV6, + .name = "TCPMSS", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_tcpmss_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_tcpmss_info)), + .help = TCPMSS_help6, + .parse = TCPMSS_parse6, + .final_check = TCPMSS_check, + .print = TCPMSS_print, + .save = TCPMSS_save, + .extra_opts = TCPMSS_opts, +}; + +void _init(void) +{ + xtables_register_target(&tcpmss_target); + xtables_register_target(&tcpmss_target6); +} diff --git a/extensions/libxt_TCPMSS.man b/extensions/libxt_TCPMSS.man new file mode 100644 index 0000000..ac8fb4e --- /dev/null +++ b/extensions/libxt_TCPMSS.man @@ -0,0 +1,47 @@ +This target allows to alter the MSS value of TCP SYN packets, to control +the maximum size for that connection (usually limiting it to your +outgoing interface's MTU minus 40 for IPv4 or 60 for IPv6, respectively). +Of course, it can only be used +in conjunction with +\fB\-p tcp\fP. +.PP +This target is used to overcome criminally braindead ISPs or servers +which block "ICMP Fragmentation Needed" or "ICMPv6 Packet Too Big" +packets. The symptoms of this +problem are that everything works fine from your Linux +firewall/router, but machines behind it can never exchange large +packets: +.PD 0 +.RS 0.1i +.TP 0.3i +1) +Web browsers connect, then hang with no data received. +.TP +2) +Small mail works fine, but large emails hang. +.TP +3) +ssh works fine, but scp hangs after initial handshaking. +.RE +.PD +Workaround: activate this option and add a rule to your firewall +configuration like: +.IP + iptables \-t mangle \-A FORWARD \-p tcp \-\-tcp\-flags SYN,RST SYN + \-j TCPMSS \-\-clamp\-mss\-to\-pmtu +.TP +\fB\-\-set\-mss\fP \fIvalue\fP +Explicitly sets MSS option to specified value. If the MSS of the packet is +already lower than \fIvalue\fP, it will \fBnot\fP be increased (from Linux +2.6.25 onwards) to avoid more problems with hosts relying on a proper MSS. +.TP +\fB\-\-clamp\-mss\-to\-pmtu\fP +Automatically clamp MSS value to (path_MTU \- 40 for IPv4; \-60 for IPv6). +This may not function as desired where asymmetric routes with differing +path MTU exist \(em the kernel uses the path MTU which it would use to send +packets from itself to the source and destination IP addresses. Prior to +Linux 2.6.25, only the path MTU to the destination IP address was +considered by this option; subsequent kernels also consider the path MTU +to the source IP address. +.PP +These options are mutually exclusive. diff --git a/extensions/libxt_TCPOPTSTRIP.c b/extensions/libxt_TCPOPTSTRIP.c new file mode 100644 index 0000000..a063d0d --- /dev/null +++ b/extensions/libxt_TCPOPTSTRIP.c @@ -0,0 +1,198 @@ +/* + * Shared library add-on to iptables to add TCPOPTSTRIP target support. + * Copyright (c) 2007 Sven Schnelle <svens@bitebene.org> + * Copyright © CC Computer Consultants GmbH, 2007 + * Jan Engelhardt <jengelh@computergmbh.de> + */ +#include <getopt.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <xtables.h> +#include <netinet/tcp.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_TCPOPTSTRIP.h> +#ifndef TCPOPT_MD5SIG +# define TCPOPT_MD5SIG 19 +#endif + +enum { + FLAG_STRIP = 1 << 0, +}; + +struct tcp_optionmap { + const char *name, *desc; + const unsigned int option; +}; + +static const struct option tcpoptstrip_tg_opts[] = { + {.name = "strip-options", .has_arg = true, .val = 's'}, + { .name = NULL } +}; + +static const struct tcp_optionmap tcp_optionmap[] = { + {"wscale", "Window scale", TCPOPT_WINDOW}, + {"mss", "Maximum Segment Size", TCPOPT_MAXSEG}, + {"sack-permitted", "SACK permitted", TCPOPT_SACK_PERMITTED}, + {"sack", "Selective ACK", TCPOPT_SACK}, + {"timestamp", "Timestamp", TCPOPT_TIMESTAMP}, + {"md5", "MD5 signature", TCPOPT_MD5SIG}, + { .name = NULL } +}; + +static void tcpoptstrip_tg_help(void) +{ + const struct tcp_optionmap *w; + + printf( +"TCPOPTSTRIP target options:\n" +" --strip-options value strip specified TCP options denoted by value\n" +" (separated by comma) from TCP header\n" +" Instead of the numeric value, you can also use the following names:\n" + ); + + for (w = tcp_optionmap; w->name != NULL; ++w) + printf(" %-14s strip \"%s\" option\n", w->name, w->desc); +} + +static void tcpoptstrip_tg_init(struct xt_entry_target *t) +{ + struct xt_tcpoptstrip_target_info *info = (void *)t->data; + + /* strictly necessary? play safe for now. */ + memset(info->strip_bmap, 0, sizeof(info->strip_bmap)); +} + +static void parse_list(struct xt_tcpoptstrip_target_info *info, char *arg) +{ + unsigned int option; + char *p; + int i; + + while (true) { + p = strchr(arg, ','); + if (p != NULL) + *p = '\0'; + + option = 0; + for (i = 0; tcp_optionmap[i].name != NULL; ++i) + if (strcmp(tcp_optionmap[i].name, arg) == 0) { + option = tcp_optionmap[i].option; + break; + } + + if (option == 0 && + !xtables_strtoui(arg, NULL, &option, 0, UINT8_MAX)) + xtables_error(PARAMETER_PROBLEM, + "Bad TCP option value \"%s\"", arg); + + if (option < 2) + xtables_error(PARAMETER_PROBLEM, + "Option value may not be 0 or 1"); + + if (tcpoptstrip_test_bit(info->strip_bmap, option)) + xtables_error(PARAMETER_PROBLEM, + "Option \"%s\" already specified", arg); + + tcpoptstrip_set_bit(info->strip_bmap, option); + if (p == NULL) + break; + arg = p + 1; + } +} + +static int tcpoptstrip_tg_parse(int c, char **argv, int invert, + unsigned int *flags, const void *entry, + struct xt_entry_target **target) +{ + struct xt_tcpoptstrip_target_info *info = (void *)(*target)->data; + + switch (c) { + case 's': + if (*flags & FLAG_STRIP) + xtables_error(PARAMETER_PROBLEM, + "You can specify --strip-options only once"); + parse_list(info, optarg); + *flags |= FLAG_STRIP; + return true; + } + + return false; +} + +static void tcpoptstrip_tg_check(unsigned int flags) +{ + if (flags == 0) + xtables_error(PARAMETER_PROBLEM, + "TCPOPTSTRIP: --strip-options parameter required"); +} + +static void +tcpoptstrip_print_list(const struct xt_tcpoptstrip_target_info *info, + bool numeric) +{ + unsigned int i, j; + const char *name; + bool first = true; + + for (i = 0; i < 256; ++i) { + if (!tcpoptstrip_test_bit(info->strip_bmap, i)) + continue; + if (!first) + printf(","); + + first = false; + name = NULL; + if (!numeric) + for (j = 0; tcp_optionmap[j].name != NULL; ++j) + if (tcp_optionmap[j].option == i) + name = tcp_optionmap[j].name; + + if (name != NULL) + printf("%s", name); + else + printf("%u", i); + } +} + +static void +tcpoptstrip_tg_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_tcpoptstrip_target_info *info = + (const void *)target->data; + + printf("TCPOPTSTRIP options "); + tcpoptstrip_print_list(info, numeric); +} + +static void +tcpoptstrip_tg_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_tcpoptstrip_target_info *info = + (const void *)target->data; + + printf("--strip-options "); + tcpoptstrip_print_list(info, true); +} + +static struct xtables_target tcpoptstrip_tg_reg = { + .version = XTABLES_VERSION, + .name = "TCPOPTSTRIP", + .family = NFPROTO_UNSPEC, + .size = XT_ALIGN(sizeof(struct xt_tcpoptstrip_target_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_tcpoptstrip_target_info)), + .help = tcpoptstrip_tg_help, + .init = tcpoptstrip_tg_init, + .parse = tcpoptstrip_tg_parse, + .final_check = tcpoptstrip_tg_check, + .print = tcpoptstrip_tg_print, + .save = tcpoptstrip_tg_save, + .extra_opts = tcpoptstrip_tg_opts, +}; + +void _init(void) +{ + xtables_register_target(&tcpoptstrip_tg_reg); +} diff --git a/extensions/libxt_TCPOPTSTRIP.man b/extensions/libxt_TCPOPTSTRIP.man new file mode 100644 index 0000000..2a07709 --- /dev/null +++ b/extensions/libxt_TCPOPTSTRIP.man @@ -0,0 +1,7 @@ +This target will strip TCP options off a TCP packet. (It will actually replace +them by NO-OPs.) As such, you will need to add the \fB\-p tcp\fP parameters. +.TP +\fB\-\-strip\-options\fP \fIoption\fP[\fB,\fP\fIoption\fP...] +Strip the given option(s). The options may be specified by TCP option number or +by symbolic name. The list of recognized options can be obtained by calling +iptables with \fB\-j TCPOPTSTRIP \-h\fP. diff --git a/extensions/libxt_TEE.c b/extensions/libxt_TEE.c new file mode 100644 index 0000000..e4c0607 --- /dev/null +++ b/extensions/libxt_TEE.c @@ -0,0 +1,202 @@ +/* + * "TEE" target extension for iptables + * Copyright © Sebastian Claßen <sebastian.classen [at] freenet.ag>, 2007 + * Jan Engelhardt <jengelh [at] medozas de>, 2007 - 2010 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License; either + * version 2 of the License, or any later version, as published by the + * Free Software Foundation. + */ +#include <sys/socket.h> +#include <getopt.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <arpa/inet.h> +#include <net/if.h> +#include <netinet/in.h> + +#include <xtables.h> +#include <linux/netfilter.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_TEE.h> + +enum { + FLAG_GATEWAY = 1 << 0, + FLAG_OIF = 1 << 1, +}; + +static const struct option tee_tg_opts[] = { + {.name = "gateway", .has_arg = true, .val = 'g'}, + {.name = "oif", .has_arg = true, .val = 'o'}, + {NULL}, +}; + +static void tee_tg_help(void) +{ + printf( +"TEE target options:\n" +" --gateway IPADDR Route packet via the gateway given by address\n" +" --oif NAME Include oif in route calculation\n" +"\n"); +} + +static int tee_tg_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_tee_tginfo *info = (void *)(*target)->data; + const struct in_addr *ia; + + switch (c) { + case 'g': + if (*flags & FLAG_GATEWAY) + xtables_error(PARAMETER_PROBLEM, + "Cannot specify --gateway more than once"); + + ia = xtables_numeric_to_ipaddr(optarg); + if (ia == NULL) + xtables_error(PARAMETER_PROBLEM, + "Invalid IP address %s", optarg); + + memcpy(&info->gw, ia, sizeof(*ia)); + *flags |= FLAG_GATEWAY; + return true; + case 'o': + if (*flags & FLAG_OIF) + xtables_error(PARAMETER_PROBLEM, + "Cannot specify --oif more than once"); + if (strlen(optarg) >= sizeof(info->oif)) + xtables_error(PARAMETER_PROBLEM, + "oif name too long"); + strcpy(info->oif, optarg); + *flags |= FLAG_OIF; + return true; + } + + return false; +} + +static int tee_tg6_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_tee_tginfo *info = (void *)(*target)->data; + const struct in6_addr *ia; + + switch (c) { + case 'g': + if (*flags & FLAG_GATEWAY) + xtables_error(PARAMETER_PROBLEM, + "Cannot specify --gateway more than once"); + + ia = xtables_numeric_to_ip6addr(optarg); + if (ia == NULL) + xtables_error(PARAMETER_PROBLEM, + "Invalid IP address %s", optarg); + + memcpy(&info->gw, ia, sizeof(*ia)); + *flags |= FLAG_GATEWAY; + return true; + case 'o': + if (*flags & FLAG_OIF) + xtables_error(PARAMETER_PROBLEM, + "Cannot specify --oif more than once"); + if (strlen(optarg) >= sizeof(info->oif)) + xtables_error(PARAMETER_PROBLEM, + "oif name too long"); + strcpy(info->oif, optarg); + *flags |= FLAG_OIF; + return true; + } + + return false; +} + +static void tee_tg_check(unsigned int flags) +{ + if (flags == 0) + xtables_error(PARAMETER_PROBLEM, "TEE target: " + "--gateway parameter required"); +} + +static void tee_tg_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_tee_tginfo *info = (const void *)target->data; + + if (numeric) + printf("TEE gw:%s ", xtables_ipaddr_to_numeric(&info->gw.in)); + else + printf("TEE gw:%s ", xtables_ipaddr_to_anyname(&info->gw.in)); + if (*info->oif != '\0') + printf("oif=%s ", info->oif); +} + +static void tee_tg6_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_tee_tginfo *info = (const void *)target->data; + + if (numeric) + printf("TEE gw:%s ", xtables_ip6addr_to_numeric(&info->gw.in6)); + else + printf("TEE gw:%s ", xtables_ip6addr_to_anyname(&info->gw.in6)); + if (*info->oif != '\0') + printf("oif=%s ", info->oif); +} + +static void tee_tg_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_tee_tginfo *info = (const void *)target->data; + + printf("--gateway %s ", xtables_ipaddr_to_numeric(&info->gw.in)); + if (*info->oif != '\0') + printf("--oif %s ", info->oif); +} + +static void tee_tg6_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_tee_tginfo *info = (const void *)target->data; + + printf("--gateway %s ", xtables_ip6addr_to_numeric(&info->gw.in6)); + if (*info->oif != '\0') + printf("--oif %s ", info->oif); +} + +static struct xtables_target tee_tg_reg = { + .name = "TEE", + .version = XTABLES_VERSION, + .revision = 1, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct xt_tee_tginfo)), + .userspacesize = XT_ALIGN(sizeof(struct xt_tee_tginfo)), + .help = tee_tg_help, + .parse = tee_tg_parse, + .final_check = tee_tg_check, + .print = tee_tg_print, + .save = tee_tg_save, + .extra_opts = tee_tg_opts, +}; + +static struct xtables_target tee_tg6_reg = { + .name = "TEE", + .version = XTABLES_VERSION, + .revision = 1, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct xt_tee_tginfo)), + .userspacesize = XT_ALIGN(sizeof(struct xt_tee_tginfo)), + .help = tee_tg_help, + .parse = tee_tg6_parse, + .final_check = tee_tg_check, + .print = tee_tg6_print, + .save = tee_tg6_save, + .extra_opts = tee_tg_opts, +}; + +void _init(void) +{ + xtables_register_target(&tee_tg_reg); + xtables_register_target(&tee_tg6_reg); +} diff --git a/extensions/libxt_TEE.man b/extensions/libxt_TEE.man new file mode 100644 index 0000000..456d150 --- /dev/null +++ b/extensions/libxt_TEE.man @@ -0,0 +1,12 @@ +The \fBTEE\fP target will clone a packet and redirect this clone to another +machine on the \fBlocal\fP network segment. In other words, the nexthop +must be the target, or you will have to configure the nexthop to forward it +further if so desired. +.TP +\fB\-\-gateway\fP \fIipaddr\fP +Send the cloned packet to the host reachable at the given IP address. +Use of 0.0.0.0 (for IPv4 packets) or :: (IPv6) is invalid. +.PP +To forward all incoming traffic on eth0 to an Network Layer logging box: +.PP +\-t mangle \-A PREROUTING \-i eth0 \-j TEE \-\-gateway 2001:db8::1 diff --git a/extensions/libxt_TOS.c b/extensions/libxt_TOS.c new file mode 100644 index 0000000..dc60cc0 --- /dev/null +++ b/extensions/libxt_TOS.c @@ -0,0 +1,245 @@ +/* + * Shared library add-on to iptables to add TOS target support + * + * Copyright © CC Computer Consultants GmbH, 2007 + * Contact: Jan Engelhardt <jengelh@computergmbh.de> + */ +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/in.h> + +#include <xtables.h> +#include <linux/netfilter/xt_DSCP.h> +#include "tos_values.c" + +struct ipt_tos_target_info { + u_int8_t tos; +}; + +enum { + FLAG_TOS = 1 << 0, +}; + +static const struct option tos_tg_opts_v0[] = { + {.name = "set-tos", .has_arg = true, .val = '='}, + { .name = NULL } +}; + +static const struct option tos_tg_opts[] = { + {.name = "set-tos", .has_arg = true, .val = '='}, + {.name = "and-tos", .has_arg = true, .val = '&'}, + {.name = "or-tos", .has_arg = true, .val = '|'}, + {.name = "xor-tos", .has_arg = true, .val = '^'}, + { .name = NULL } +}; + +static void tos_tg_help_v0(void) +{ + const struct tos_symbol_info *symbol; + + printf( +"TOS target options:\n" +" --set-tos value Set Type of Service/Priority field to value\n" +" --set-tos symbol Set TOS field (IPv4 only) by symbol\n" +" Accepted symbolic names for value are:\n"); + + for (symbol = tos_symbol_names; symbol->name != NULL; ++symbol) + printf(" (0x%02x) %2u %s\n", + symbol->value, symbol->value, symbol->name); + + printf("\n"); +} + +static void tos_tg_help(void) +{ + const struct tos_symbol_info *symbol; + + printf( +"TOS target v%s options:\n" +" --set-tos value[/mask] Set Type of Service/Priority field to value\n" +" (Zero out bits in mask and XOR value into TOS)\n" +" --set-tos symbol Set TOS field (IPv4 only) by symbol\n" +" (this zeroes the 4-bit Precedence part!)\n" +" Accepted symbolic names for value are:\n", +XTABLES_VERSION); + + for (symbol = tos_symbol_names; symbol->name != NULL; ++symbol) + printf(" (0x%02x) %2u %s\n", + symbol->value, symbol->value, symbol->name); + + printf( +"\n" +" --and-tos bits Binary AND the TOS value with bits\n" +" --or-tos bits Binary OR the TOS value with bits\n" +" --xor-tos bits Binary XOR the TOS value with bits\n" +); +} + +static int tos_tg_parse_v0(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct ipt_tos_target_info *info = (void *)(*target)->data; + struct tos_value_mask tvm; + + switch (c) { + case '=': + xtables_param_act(XTF_ONLY_ONCE, "TOS", "--set-tos", *flags & FLAG_TOS); + xtables_param_act(XTF_NO_INVERT, "TOS", "--set-tos", invert); + if (!tos_parse_symbolic(optarg, &tvm, 0xFF)) + xtables_param_act(XTF_BAD_VALUE, "TOS", "--set-tos", optarg); + if (tvm.mask != 0xFF) + xtables_error(PARAMETER_PROBLEM, "tos match: Your kernel " + "is too old to support anything besides " + "/0xFF as a mask."); + info->tos = tvm.value; + *flags |= FLAG_TOS; + return true; + } + + return false; +} + +static int tos_tg_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_tos_target_info *info = (void *)(*target)->data; + struct tos_value_mask tvm; + unsigned int bits; + + switch (c) { + case '=': /* --set-tos */ + xtables_param_act(XTF_ONLY_ONCE, "TOS", "--set-tos", *flags & FLAG_TOS); + xtables_param_act(XTF_NO_INVERT, "TOS", "--set-tos", invert); + if (!tos_parse_symbolic(optarg, &tvm, 0x3F)) + xtables_param_act(XTF_BAD_VALUE, "TOS", "--set-tos", optarg); + info->tos_value = tvm.value; + info->tos_mask = tvm.mask; + break; + + case '&': /* --and-tos */ + xtables_param_act(XTF_ONLY_ONCE, "TOS", "--and-tos", *flags & FLAG_TOS); + xtables_param_act(XTF_NO_INVERT, "TOS", "--and-tos", invert); + if (!xtables_strtoui(optarg, NULL, &bits, 0, UINT8_MAX)) + xtables_param_act(XTF_BAD_VALUE, "TOS", "--and-tos", optarg); + info->tos_value = 0; + info->tos_mask = ~bits; + break; + + case '|': /* --or-tos */ + xtables_param_act(XTF_ONLY_ONCE, "TOS", "--or-tos", *flags & FLAG_TOS); + xtables_param_act(XTF_NO_INVERT, "TOS", "--or-tos", invert); + if (!xtables_strtoui(optarg, NULL, &bits, 0, UINT8_MAX)) + xtables_param_act(XTF_BAD_VALUE, "TOS", "--or-tos", optarg); + info->tos_value = bits; + info->tos_mask = bits; + break; + + case '^': /* --xor-tos */ + xtables_param_act(XTF_ONLY_ONCE, "TOS", "--xor-tos", *flags & FLAG_TOS); + xtables_param_act(XTF_NO_INVERT, "TOS", "--xor-tos", invert); + if (!xtables_strtoui(optarg, NULL, &bits, 0, UINT8_MAX)) + xtables_param_act(XTF_BAD_VALUE, "TOS", "--xor-tos", optarg); + info->tos_value = bits; + info->tos_mask = 0; + break; + + default: + return false; + } + + *flags |= FLAG_TOS; + return true; +} + +static void tos_tg_check(unsigned int flags) +{ + if (flags == 0) + xtables_error(PARAMETER_PROBLEM, + "TOS: The --set-tos parameter is required"); +} + +static void tos_tg_print_v0(const void *ip, + const struct xt_entry_target *target, int numeric) +{ + const struct ipt_tos_target_info *info = (const void *)target->data; + + printf("TOS set "); + if (numeric || !tos_try_print_symbolic("", info->tos, 0xFF)) + printf("0x%02x ", info->tos); +} + +static void tos_tg_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_tos_target_info *info = (const void *)target->data; + + if (numeric) + printf("TOS set 0x%02x/0x%02x ", + info->tos_value, info->tos_mask); + else if (tos_try_print_symbolic("TOS set ", + info->tos_value, info->tos_mask)) + /* already printed by call */ + return; + else if (info->tos_value == 0) + printf("TOS and 0x%02x ", + (unsigned int)(u_int8_t)~info->tos_mask); + else if (info->tos_value == info->tos_mask) + printf("TOS or 0x%02x ", info->tos_value); + else if (info->tos_mask == 0) + printf("TOS xor 0x%02x ", info->tos_value); + else + printf("TOS set 0x%02x/0x%02x ", + info->tos_value, info->tos_mask); +} + +static void tos_tg_save_v0(const void *ip, const struct xt_entry_target *target) +{ + const struct ipt_tos_target_info *info = (const void *)target->data; + + printf("--set-tos 0x%02x ", info->tos); +} + +static void tos_tg_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_tos_target_info *info = (const void *)target->data; + + printf("--set-tos 0x%02x/0x%02x ", info->tos_value, info->tos_mask); +} + +static struct xtables_target tos_tg_reg[] = { + { + .version = XTABLES_VERSION, + .name = "TOS", + .revision = 0, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct xt_tos_target_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_tos_target_info)), + .help = tos_tg_help_v0, + .parse = tos_tg_parse_v0, + .final_check = tos_tg_check, + .print = tos_tg_print_v0, + .save = tos_tg_save_v0, + .extra_opts = tos_tg_opts_v0, + }, + { + .version = XTABLES_VERSION, + .name = "TOS", + .revision = 1, + .family = NFPROTO_UNSPEC, + .size = XT_ALIGN(sizeof(struct xt_tos_target_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_tos_target_info)), + .help = tos_tg_help, + .parse = tos_tg_parse, + .final_check = tos_tg_check, + .print = tos_tg_print, + .save = tos_tg_save, + .extra_opts = tos_tg_opts, + }, +}; + +void _init(void) +{ + xtables_register_targets(tos_tg_reg, ARRAY_SIZE(tos_tg_reg)); +} diff --git a/extensions/libxt_TOS.man b/extensions/libxt_TOS.man new file mode 100644 index 0000000..d5cbfcb --- /dev/null +++ b/extensions/libxt_TOS.man @@ -0,0 +1,27 @@ +This module sets the Type of Service field in the IPv4 header (including the +"precedence" bits) or the Priority field in the IPv6 header. Note that TOS +shares the same bits as DSCP and ECN. The TOS target is only valid in the +\fBmangle\fR table. +.TP +\fB\-\-set\-tos\fP \fIvalue\fP[\fB/\fP\fImask\fP] +Zeroes out the bits given by \fImask\fR and XORs \fIvalue\fR into the +TOS/Priority field. If \fImask\fR is omitted, 0xFF is assumed. +.TP +\fB\-\-set\-tos\fP \fIsymbol\fP +You can specify a symbolic name when using the TOS target for IPv4. It implies +a mask of 0xFF. The list of recognized TOS names can be obtained by calling +iptables with \fB\-j TOS \-h\fP. +.PP +The following mnemonics are available: +.TP +\fB\-\-and\-tos\fP \fIbits\fP +Binary AND the TOS value with \fIbits\fR. (Mnemonic for \fB\-\-set\-tos +0/\fR\fIinvbits\fR, where \fIinvbits\fR is the binary negation of \fIbits\fR.) +.TP +\fB\-\-or\-tos\fP \fIbits\fP +Binary OR the TOS value with \fIbits\fR. (Mnemonic for \fB\-\-set\-tos\fP +\fIbits\fR\fB/\fR\fIbits\fR.) +.TP +\fB\-\-xor\-tos\fP \fIbits\fP +Binary XOR the TOS value with \fIbits\fR. (Mnemonic for \fB\-\-set\-tos\fP +\fIbits\fR\fB/0\fR.) diff --git a/extensions/libxt_TPROXY.c b/extensions/libxt_TPROXY.c new file mode 100644 index 0000000..d410c52 --- /dev/null +++ b/extensions/libxt_TPROXY.c @@ -0,0 +1,150 @@ +/* + * Shared library add-on to iptables to add TPROXY target support. + * + * Copyright (C) 2002-2008 BalaBit IT Ltd. + */ +#include <getopt.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <limits.h> + +#include <xtables.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_TPROXY.h> + +static const struct option tproxy_tg_opts[] = { + { .name = "on-port", .has_arg = 1, .val = '1'}, + { .name = "on-ip", .has_arg = 1, .val = '2'}, + { .name = "tproxy-mark", .has_arg = 1, .val = '3'}, + {NULL}, +}; + +enum { + PARAM_ONPORT = 1 << 0, + PARAM_ONIP = 1 << 1, + PARAM_MARK = 1 << 2, +}; + +static void tproxy_tg_help(void) +{ + printf( +"TPROXY target options:\n" +" --on-port port Redirect connection to port, or the original port if 0\n" +" --on-ip ip Optionally redirect to the given IP\n" +" --tproxy-mark value[/mask] Mark packets with the given value/mask\n\n"); +} + +static void parse_tproxy_lport(const char *s, struct xt_tproxy_target_info *info) +{ + unsigned int lport; + + if (xtables_strtoui(s, NULL, &lport, 0, UINT16_MAX)) + info->lport = htons(lport); + else + xtables_param_act(XTF_BAD_VALUE, "TPROXY", "--on-port", s); +} + +static void parse_tproxy_laddr(const char *s, struct xt_tproxy_target_info *info) +{ + struct in_addr *laddr; + + if ((laddr = xtables_numeric_to_ipaddr(s)) == NULL) + xtables_param_act(XTF_BAD_VALUE, "TPROXY", "--on-ip", s); + + info->laddr = laddr->s_addr; +} + +static void parse_tproxy_mark(char *s, struct xt_tproxy_target_info *info) +{ + unsigned int value, mask = UINT32_MAX; + char *end; + + if (!xtables_strtoui(s, &end, &value, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "TPROXY", "--tproxy-mark", s); + if (*end == '/') + if (!xtables_strtoui(end + 1, &end, &mask, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "TPROXY", "--tproxy-mark", s); + if (*end != '\0') + xtables_param_act(XTF_BAD_VALUE, "TPROXY", "--tproxy-mark", s); + + info->mark_mask = mask; + info->mark_value = value; +} + +static int tproxy_tg_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + struct xt_tproxy_target_info *tproxyinfo = (void *)(*target)->data; + + switch (c) { + case '1': + xtables_param_act(XTF_ONLY_ONCE, "TPROXY", "--on-port", *flags & PARAM_ONPORT); + xtables_param_act(XTF_NO_INVERT, "TPROXY", "--on-port", invert); + parse_tproxy_lport(optarg, tproxyinfo); + *flags |= PARAM_ONPORT; + return 1; + case '2': + xtables_param_act(XTF_ONLY_ONCE, "TPROXY", "--on-ip", *flags & PARAM_ONIP); + xtables_param_act(XTF_NO_INVERT, "TPROXY", "--on-ip", invert); + parse_tproxy_laddr(optarg, tproxyinfo); + *flags |= PARAM_ONIP; + return 1; + case '3': + xtables_param_act(XTF_ONLY_ONCE, "TPROXY", "--tproxy-mark", *flags & PARAM_MARK); + xtables_param_act(XTF_NO_INVERT, "TPROXY", "--tproxy-mark", invert); + parse_tproxy_mark(optarg, tproxyinfo); + *flags |= PARAM_MARK; + return 1; + } + + return 0; +} + +static void tproxy_tg_check(unsigned int flags) +{ + if (!(flags & PARAM_ONPORT)) + xtables_error(PARAMETER_PROBLEM, + "TPROXY target: Parameter --on-port is required"); +} + +static void tproxy_tg_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct xt_tproxy_target_info *info = (const void *)target->data; + printf("TPROXY redirect %s:%u mark 0x%x/0x%x", + xtables_ipaddr_to_numeric((const struct in_addr *)&info->laddr), + ntohs(info->lport), (unsigned int)info->mark_value, + (unsigned int)info->mark_mask); +} + +static void tproxy_tg_save(const void *ip, const struct xt_entry_target *target) +{ + const struct xt_tproxy_target_info *info = (const void *)target->data; + + printf("--on-port %u ", ntohs(info->lport)); + printf("--on-ip %s ", + xtables_ipaddr_to_numeric((const struct in_addr *)&info->laddr)); + printf("--tproxy-mark 0x%x/0x%x ", + (unsigned int)info->mark_value, (unsigned int)info->mark_mask); +} + +static struct xtables_target tproxy_tg_reg = { + .name = "TPROXY", + .family = NFPROTO_IPV4, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_tproxy_target_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_tproxy_target_info)), + .help = tproxy_tg_help, + .parse = tproxy_tg_parse, + .final_check = tproxy_tg_check, + .print = tproxy_tg_print, + .save = tproxy_tg_save, + .extra_opts = tproxy_tg_opts, +}; + +void _init(void) +{ + xtables_register_target(&tproxy_tg_reg); +} diff --git a/extensions/libxt_TPROXY.man b/extensions/libxt_TPROXY.man new file mode 100644 index 0000000..0129f84 --- /dev/null +++ b/extensions/libxt_TPROXY.man @@ -0,0 +1,21 @@ +This target is only valid in the \fBmangle\fR table, in the \fBPREROUTING\fR +chain and user-defined chains which are only called from this chain. It +redirects the packet to a local socket without changing the packet header in +any way. It can also change the mark value which can then be used in advanced +routing rules. +It takes three options: +.TP +\fB\-\-on\-port\fP \fIport\fP +This specifies a destination port to use. It is a required option, 0 means the +new destination port is the same as the original. This is only valid if the +rule also specifies \fB\-p tcp\fP or \fB\-p udp\fP. +.TP +\fB\-\-on\-ip\fP \fIaddress\fP +This specifies a destination address to use. By default the address is the IP +address of the incoming interface. This is only valid if the rule also +specifies \fB\-p tcp\fP or \fB\-p udp\fP. +.TP +\fB\-\-tproxy\-mark\fP \fIvalue\fP[\fB/\fP\fImask\fP] +Marks packets with the given value/mask. The fwmark value set here can be used +by advanced routing. (Required for transparent proxying to work: otherwise +these packets will get forwarded, which is probably not what you want.) diff --git a/extensions/libxt_TRACE.c b/extensions/libxt_TRACE.c new file mode 100644 index 0000000..0282e6f --- /dev/null +++ b/extensions/libxt_TRACE.c @@ -0,0 +1,21 @@ +/* Shared library add-on to iptables to add TRACE target support. */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/x_tables.h> + +static struct xtables_target trace_target = { + .family = NFPROTO_UNSPEC, + .name = "TRACE", + .version = XTABLES_VERSION, + .size = XT_ALIGN(0), + .userspacesize = XT_ALIGN(0), +}; + +void _init(void) +{ + xtables_register_target(&trace_target); +} diff --git a/extensions/libxt_TRACE.man b/extensions/libxt_TRACE.man new file mode 100644 index 0000000..d28c3a0 --- /dev/null +++ b/extensions/libxt_TRACE.man @@ -0,0 +1,11 @@ +This target marks packes so that the kernel will log every rule which match +the packets as those traverse the tables, chains, rules. (The ipt_LOG or +ip6t_LOG module +is required for the logging.) The packets are logged with the string prefix: +"TRACE: tablename:chainname:type:rulenum " where type can be "rule" for +plain rule, "return" for implicit rule at the end of a user defined chain +and "policy" for the policy of the built in chains. +.br +It can only be used in the +.BR raw +table. diff --git a/extensions/libxt_cluster.c b/extensions/libxt_cluster.c new file mode 100644 index 0000000..ea5d9fb --- /dev/null +++ b/extensions/libxt_cluster.c @@ -0,0 +1,238 @@ +/* + * (C) 2009 by Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <stddef.h> + +#include <xtables.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_cluster.h> + +/* hack to keep for check */ +static unsigned int total_nodes; +static unsigned int node_mask; + +static void +cluster_help(void) +{ + printf( +"cluster match options:\n" +" --cluster-total-nodes <num> Set number of total nodes in cluster\n" +" [!] --cluster-local-node <num> Set the local node number\n" +" [!] --cluster-local-nodemask <num> Set the local node mask\n" +" --cluster-hash-seed <num> Set seed value of the Jenkins hash\n"); +} + +enum { + CLUSTER_OPT_TOTAL_NODES, + CLUSTER_OPT_LOCAL_NODE, + CLUSTER_OPT_NODE_MASK, + CLUSTER_OPT_HASH_SEED, +}; + +static const struct option cluster_opts[] = { + { "cluster-total-nodes", 1, NULL, CLUSTER_OPT_TOTAL_NODES }, + { "cluster-local-node", 1, NULL, CLUSTER_OPT_LOCAL_NODE }, + { "cluster-local-nodemask", 1, NULL, CLUSTER_OPT_NODE_MASK }, + { "cluster-hash-seed", 1, NULL, CLUSTER_OPT_HASH_SEED }, + { .name = NULL } +}; + +static int +cluster_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_cluster_match_info *info = (void *)(*match)->data; + unsigned int num; + + switch (c) { + case CLUSTER_OPT_TOTAL_NODES: + if (*flags & (1 << c)) { + xtables_error(PARAMETER_PROBLEM, + "Can only specify " + "`--cluster-total-nodes' once"); + } + if (!xtables_strtoui(optarg, NULL, &num, 1, + XT_CLUSTER_NODES_MAX)) { + xtables_error(PARAMETER_PROBLEM, + "Unable to parse `%s' in " + "`--cluster-total-nodes'", optarg); + } + total_nodes = num; + info->total_nodes = total_nodes = num; + *flags |= 1 << c; + break; + case CLUSTER_OPT_LOCAL_NODE: + if (*flags & (1 << c)) { + xtables_error(PARAMETER_PROBLEM, + "Can only specify " + "`--cluster-local-node' once"); + } + if (*flags & (1 << CLUSTER_OPT_NODE_MASK)) { + xtables_error(PARAMETER_PROBLEM, "You cannot use " + "`--cluster-local-nodemask' and " + "`--cluster-local-node'"); + } + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + if (!xtables_strtoui(optarg, NULL, &num, 1, + XT_CLUSTER_NODES_MAX)) { + xtables_error(PARAMETER_PROBLEM, + "Unable to parse `%s' in " + "`--cluster-local-node'", optarg); + } + if (invert) + info->flags |= (1 << XT_CLUSTER_F_INV); + + info->node_mask = node_mask = (1 << (num - 1)); + *flags |= 1 << c; + break; + case CLUSTER_OPT_NODE_MASK: + if (*flags & (1 << c)) { + xtables_error(PARAMETER_PROBLEM, + "Can only specify " + "`--cluster-local-node' once"); + } + if (*flags & (1 << CLUSTER_OPT_LOCAL_NODE)) { + xtables_error(PARAMETER_PROBLEM, "You cannot use " + "`--cluster-local-nodemask' and " + "`--cluster-local-node'"); + } + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + if (!xtables_strtoui(optarg, NULL, &num, 1, + XT_CLUSTER_NODES_MAX)) { + xtables_error(PARAMETER_PROBLEM, + "Unable to parse `%s' in " + "`--cluster-local-node'", optarg); + } + if (invert) + info->flags |= (1 << XT_CLUSTER_F_INV); + + info->node_mask = node_mask = num; + *flags |= 1 << c; + break; + + case CLUSTER_OPT_HASH_SEED: + if (*flags & (1 << c)) { + xtables_error(PARAMETER_PROBLEM, + "Can only specify " + "`--cluster-hash-seed' once"); + } + if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX)) { + xtables_error(PARAMETER_PROBLEM, + "Unable to parse `%s'", optarg); + } + info->hash_seed = num; + *flags |= 1 << c; + break; + default: + return 0; + } + + return 1; +} + +static void +cluster_check(unsigned int flags) +{ + if ((flags & ((1 << CLUSTER_OPT_TOTAL_NODES) | + (1 << CLUSTER_OPT_LOCAL_NODE) | + (1 << CLUSTER_OPT_HASH_SEED))) + == ((1 << CLUSTER_OPT_TOTAL_NODES) | + (1 << CLUSTER_OPT_LOCAL_NODE) | + (1 << CLUSTER_OPT_HASH_SEED))) { + if (node_mask >= (1ULL << total_nodes)) { + xtables_error(PARAMETER_PROBLEM, + "cluster match: " + "`--cluster-local-node' " + "must be <= `--cluster-total-nodes'"); + } + return; + } + if ((flags & ((1 << CLUSTER_OPT_TOTAL_NODES) | + (1 << CLUSTER_OPT_NODE_MASK) | + (1 << CLUSTER_OPT_HASH_SEED))) + == ((1 << CLUSTER_OPT_TOTAL_NODES) | + (1 << CLUSTER_OPT_NODE_MASK) | + (1 << CLUSTER_OPT_HASH_SEED))) { + if (node_mask >= (1ULL << total_nodes)) { + xtables_error(PARAMETER_PROBLEM, + "cluster match: " + "`--cluster-local-nodemask' too big " + "for `--cluster-total-nodes'"); + } + return; + } + if (!(flags & (1 << CLUSTER_OPT_TOTAL_NODES))) { + xtables_error(PARAMETER_PROBLEM, + "cluster match: `--cluster-total-nodes' " + "is missing"); + } + if (!(flags & (1 << CLUSTER_OPT_HASH_SEED))) { + xtables_error(PARAMETER_PROBLEM, + "cluster match: `--cluster-hash-seed' " + "is missing"); + } + if (!(flags & ((1 << (CLUSTER_OPT_LOCAL_NODE) | + (1 << (CLUSTER_OPT_NODE_MASK)))))) { + xtables_error(PARAMETER_PROBLEM, + "cluster match: `--cluster-local-node' or" + "`--cluster-local-nodemask' is missing"); + } +} + +static void +cluster_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_cluster_match_info *info = (void *)match->data; + + printf("cluster "); + if (info->flags & XT_CLUSTER_F_INV) + printf("!node_mask=0x%08x ", info->node_mask); + else + printf("node_mask=0x%08x ", info->node_mask); + + printf("total_nodes=%u hash_seed=0x%08x ", + info->total_nodes, info->hash_seed); +} + +static void +cluster_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_cluster_match_info *info = (void *)match->data; + + if (info->flags & XT_CLUSTER_F_INV) + printf("! --cluster-local-nodemask 0x%08x ", info->node_mask); + else + printf("--cluster-local-nodemask 0x%08x ", info->node_mask); + + printf("--cluster-total-nodes %u --cluster-hash-seed 0x%08x ", + info->total_nodes, info->hash_seed); +} + +static struct xtables_match cluster_mt_reg = { + .family = NFPROTO_UNSPEC, + .name = "cluster", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_cluster_match_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_cluster_match_info)), + .help = cluster_help, + .parse = cluster_parse, + .final_check = cluster_check, + .print = cluster_print, + .save = cluster_save, + .extra_opts = cluster_opts, +}; + +void _init(void) +{ + xtables_register_match(&cluster_mt_reg); +} diff --git a/extensions/libxt_cluster.man b/extensions/libxt_cluster.man new file mode 100644 index 0000000..62ad71c --- /dev/null +++ b/extensions/libxt_cluster.man @@ -0,0 +1,62 @@ +Allows you to deploy gateway and back-end load-sharing clusters without the +need of load-balancers. +.PP +This match requires that all the nodes see the same packets. Thus, the cluster +match decides if this node has to handle a packet given the following options: +.TP +\fB\-\-cluster\-total\-nodes\fP \fInum\fP +Set number of total nodes in cluster. +.TP +[\fB!\fP] \fB\-\-cluster\-local\-node\fP \fInum\fP +Set the local node number ID. +.TP +[\fB!\fP] \fB\-\-cluster\-local\-nodemask\fP \fImask\fP +Set the local node number ID mask. You can use this option instead +of \fB\-\-cluster\-local\-node\fP. +.TP +\fB\-\-cluster\-hash\-seed\fP \fIvalue\fP +Set seed value of the Jenkins hash. +.PP +Example: +.IP +iptables \-A PREROUTING \-t mangle \-i eth1 \-m cluster +\-\-cluster\-total\-nodes 2 \-\-cluster\-local\-node 1 +\-\-cluster\-hash\-seed 0xdeadbeef +\-j MARK \-\-set-mark 0xffff +.IP +iptables \-A PREROUTING \-t mangle \-i eth2 \-m cluster +\-\-cluster\-total\-nodes 2 \-\-cluster\-local\-node 1 +\-\-cluster\-hash\-seed 0xdeadbeef +\-j MARK -\-set\-mark 0xffff +.IP +iptables \-A PREROUTING \-t mangle \-i eth1 +\-m mark ! \-\-mark 0xffff \-j DROP +.IP +iptables \-A PREROUTING \-t mangle \-i eth2 +\-m mark ! \-\-mark 0xffff \-j DROP +.PP +And the following commands to make all nodes see the same packets: +.IP +ip maddr add 01:00:5e:00:01:01 dev eth1 +.IP +ip maddr add 01:00:5e:00:01:02 dev eth2 +.IP +arptables \-A OUTPUT \-o eth1 \-\-h\-length 6 +\-j mangle \-\-mangle-mac-s 01:00:5e:00:01:01 +.IP +arptables \-A INPUT \-i eth1 \-\-h-length 6 +\-\-destination-mac 01:00:5e:00:01:01 +\-j mangle \-\-mangle\-mac\-d 00:zz:yy:xx:5a:27 +.IP +arptables \-A OUTPUT \-o eth2 \-\-h\-length 6 +\-j mangle \-\-mangle\-mac\-s 01:00:5e:00:01:02 +.IP +arptables \-A INPUT \-i eth2 \-\-h\-length 6 +\-\-destination\-mac 01:00:5e:00:01:02 +\-j mangle \-\-mangle\-mac\-d 00:zz:yy:xx:5a:27 +.PP +In the case of TCP connections, pickup facility has to be disabled +to avoid marking TCP ACK packets coming in the reply direction as +valid. +.IP +echo 0 > /proc/sys/net/netfilter/nf_conntrack_tcp_loose diff --git a/extensions/libxt_comment.c b/extensions/libxt_comment.c new file mode 100644 index 0000000..0068a6e --- /dev/null +++ b/extensions/libxt_comment.c @@ -0,0 +1,108 @@ +/* Shared library add-on to iptables to add comment match support. + * + * ChangeLog + * 2003-05-13: Brad Fisher <brad@info-link.net> + * Initial comment match + * 2004-05-12: Brad Fisher <brad@info-link.net> + * Port to patch-o-matic-ng + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/xt_comment.h> + +static void comment_help(void) +{ + printf( + "comment match options:\n" + "--comment COMMENT Attach a comment to a rule\n"); +} + +static const struct option comment_opts[] = { + { "comment", 1, NULL, '1' }, + { .name = NULL } +}; + +static void +parse_comment(const char *s, struct xt_comment_info *info) +{ + int slen = strlen(s); + + if (slen >= XT_MAX_COMMENT_LEN) { + xtables_error(PARAMETER_PROBLEM, + "COMMENT must be shorter than %i characters", XT_MAX_COMMENT_LEN); + } + strcpy((char *)info->comment, s); +} + +static int +comment_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_comment_info *commentinfo = (struct xt_comment_info *)(*match)->data; + + switch (c) { + case '1': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) { + xtables_error(PARAMETER_PROBLEM, + "Sorry, you can't have an inverted comment"); + } + parse_comment(optarg, commentinfo); + *flags = 1; + break; + + default: + return 0; + } + return 1; +} + +static void comment_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "COMMENT match: You must specify `--comment'"); +} + +static void +comment_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + struct xt_comment_info *commentinfo = (void *)match->data; + + commentinfo->comment[XT_MAX_COMMENT_LEN-1] = '\0'; + printf("/* %s */ ", commentinfo->comment); +} + +/* Saves the union ipt_matchinfo in parsable form to stdout. */ +static void +comment_save(const void *ip, const struct xt_entry_match *match) +{ + struct xt_comment_info *commentinfo = (void *)match->data; + + commentinfo->comment[XT_MAX_COMMENT_LEN-1] = '\0'; + printf("--comment "); + xtables_save_string((const char *)commentinfo->comment); +} + +static struct xtables_match comment_match = { + .family = NFPROTO_UNSPEC, + .name = "comment", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_comment_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_comment_info)), + .help = comment_help, + .parse = comment_parse, + .final_check = comment_check, + .print = comment_print, + .save = comment_save, + .extra_opts = comment_opts, +}; + +void _init(void) +{ + xtables_register_match(&comment_match); +} diff --git a/extensions/libxt_comment.man b/extensions/libxt_comment.man new file mode 100644 index 0000000..faaee2a --- /dev/null +++ b/extensions/libxt_comment.man @@ -0,0 +1,6 @@ +Allows you to add comments (up to 256 characters) to any rule. +.TP +\fB\-\-comment\fP \fIcomment\fP +.TP +Example: +iptables \-A INPUT \-i eth1 \-m comment \-\-comment "my local LAN" diff --git a/extensions/libxt_connbytes.c b/extensions/libxt_connbytes.c new file mode 100644 index 0000000..5ebdd34 --- /dev/null +++ b/extensions/libxt_connbytes.c @@ -0,0 +1,199 @@ +/* Shared library add-on to iptables to add byte tracking support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <linux/netfilter/nf_conntrack_common.h> +#include <linux/netfilter/xt_connbytes.h> + +static void connbytes_help(void) +{ + printf( +"connbytes match options:\n" +" [!] --connbytes from:[to]\n" +" --connbytes-dir [original, reply, both]\n" +" --connbytes-mode [packets, bytes, avgpkt]\n"); +} + +static const struct option connbytes_opts[] = { + { "connbytes", 1, NULL, '1' }, + { "connbytes-dir", 1, NULL, '2' }, + { "connbytes-mode", 1, NULL, '3' }, + { .name = NULL } +}; + +static void +parse_range(const char *arg, struct xt_connbytes_info *si) +{ + char *colon,*p; + + si->count.from = strtoul(arg,&colon,10); + if (*colon != ':') + xtables_error(PARAMETER_PROBLEM, "Bad range \"%s\"", arg); + si->count.to = strtoul(colon+1,&p,10); + if (p == colon+1) { + /* second number omited */ + si->count.to = 0xffffffff; + } + if (si->count.from > si->count.to) + xtables_error(PARAMETER_PROBLEM, "%llu should be less than %llu", + (unsigned long long)si->count.from, + (unsigned long long)si->count.to); +} + +static int +connbytes_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_connbytes_info *sinfo = (struct xt_connbytes_info *)(*match)->data; + unsigned long i; + + switch (c) { + case '1': + if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) + optind++; + + parse_range(optarg, sinfo); + if (invert) { + i = sinfo->count.from; + sinfo->count.from = sinfo->count.to; + sinfo->count.to = i; + } + *flags |= 1; + break; + case '2': + if (!strcmp(optarg, "original")) + sinfo->direction = XT_CONNBYTES_DIR_ORIGINAL; + else if (!strcmp(optarg, "reply")) + sinfo->direction = XT_CONNBYTES_DIR_REPLY; + else if (!strcmp(optarg, "both")) + sinfo->direction = XT_CONNBYTES_DIR_BOTH; + else + xtables_error(PARAMETER_PROBLEM, + "Unknown --connbytes-dir `%s'", optarg); + + *flags |= 2; + break; + case '3': + if (!strcmp(optarg, "packets")) + sinfo->what = XT_CONNBYTES_PKTS; + else if (!strcmp(optarg, "bytes")) + sinfo->what = XT_CONNBYTES_BYTES; + else if (!strcmp(optarg, "avgpkt")) + sinfo->what = XT_CONNBYTES_AVGPKT; + else + xtables_error(PARAMETER_PROBLEM, + "Unknown --connbytes-mode `%s'", optarg); + *flags |= 4; + break; + default: + return 0; + } + + return 1; +} + +static void connbytes_check(unsigned int flags) +{ + if (flags != 7) + xtables_error(PARAMETER_PROBLEM, "You must specify `--connbytes'" + "`--connbytes-dir' and `--connbytes-mode'"); +} + +static void print_mode(const struct xt_connbytes_info *sinfo) +{ + switch (sinfo->what) { + case XT_CONNBYTES_PKTS: + fputs("packets ", stdout); + break; + case XT_CONNBYTES_BYTES: + fputs("bytes ", stdout); + break; + case XT_CONNBYTES_AVGPKT: + fputs("avgpkt ", stdout); + break; + default: + fputs("unknown ", stdout); + break; + } +} + +static void print_direction(const struct xt_connbytes_info *sinfo) +{ + switch (sinfo->direction) { + case XT_CONNBYTES_DIR_ORIGINAL: + fputs("original ", stdout); + break; + case XT_CONNBYTES_DIR_REPLY: + fputs("reply ", stdout); + break; + case XT_CONNBYTES_DIR_BOTH: + fputs("both ", stdout); + break; + default: + fputs("unknown ", stdout); + break; + } +} + +static void +connbytes_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_connbytes_info *sinfo = (const void *)match->data; + + if (sinfo->count.from > sinfo->count.to) + printf("connbytes ! %llu:%llu ", + (unsigned long long)sinfo->count.to, + (unsigned long long)sinfo->count.from); + else + printf("connbytes %llu:%llu ", + (unsigned long long)sinfo->count.from, + (unsigned long long)sinfo->count.to); + + fputs("connbytes mode ", stdout); + print_mode(sinfo); + + fputs("connbytes direction ", stdout); + print_direction(sinfo); +} + +static void connbytes_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_connbytes_info *sinfo = (const void *)match->data; + + if (sinfo->count.from > sinfo->count.to) + printf("! --connbytes %llu:%llu ", + (unsigned long long)sinfo->count.to, + (unsigned long long)sinfo->count.from); + else + printf("--connbytes %llu:%llu ", + (unsigned long long)sinfo->count.from, + (unsigned long long)sinfo->count.to); + + fputs("--connbytes-mode ", stdout); + print_mode(sinfo); + + fputs("--connbytes-dir ", stdout); + print_direction(sinfo); +} + +static struct xtables_match connbytes_match = { + .family = NFPROTO_UNSPEC, + .name = "connbytes", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_connbytes_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_connbytes_info)), + .help = connbytes_help, + .parse = connbytes_parse, + .final_check = connbytes_check, + .print = connbytes_print, + .save = connbytes_save, + .extra_opts = connbytes_opts, +}; + +void _init(void) +{ + xtables_register_match(&connbytes_match); +} diff --git a/extensions/libxt_connbytes.man b/extensions/libxt_connbytes.man new file mode 100644 index 0000000..0504a55 --- /dev/null +++ b/extensions/libxt_connbytes.man @@ -0,0 +1,36 @@ +Match by how many bytes or packets a connection (or one of the two +flows constituting the connection) has transferred so far, or by +average bytes per packet. +.PP +The counters are 64-bit and are thus not expected to overflow ;) +.PP +The primary use is to detect long-lived downloads and mark them to be +scheduled using a lower priority band in traffic control. +.PP +The transferred bytes per connection can also be viewed through +`conntrack \-L` and accessed via ctnetlink. +.PP +NOTE that for connections which have no accounting information, the match will +always return false. The "net.netfilter.nf_conntrack_acct" sysctl flag controls +whether \fBnew\fP connections will be byte/packet counted. Existing connection +flows will not be gaining/losing a/the accounting structure when be sysctl flag +is flipped. +.TP +[\fB!\fP] \fB\-\-connbytes\fP \fIfrom\fP[\fB:\fP\fIto\fP] +match packets from a connection whose packets/bytes/average packet +size is more than FROM and less than TO bytes/packets. if TO is +omitted only FROM check is done. "!" is used to match packets not +falling in the range. +.TP +\fB\-\-connbytes\-dir\fP {\fBoriginal\fP|\fBreply\fP|\fBboth\fP} +which packets to consider +.TP +\fB\-\-connbytes\-mode\fP {\fBpackets\fP|\fBbytes\fP|\fBavgpkt\fP} +whether to check the amount of packets, number of bytes transferred or +the average size (in bytes) of all packets received so far. Note that +when "both" is used together with "avgpkt", and data is going (mainly) +only in one direction (for example HTTP), the average packet size will +be about half of the actual data packets. +.TP +Example: +iptables .. \-m connbytes \-\-connbytes 10000:100000 \-\-connbytes\-dir both \-\-connbytes\-mode bytes ... diff --git a/extensions/libxt_connlimit.c b/extensions/libxt_connlimit.c new file mode 100644 index 0000000..a215915 --- /dev/null +++ b/extensions/libxt_connlimit.c @@ -0,0 +1,216 @@ +/* Shared library add-on to iptables to add connection limit support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <getopt.h> +#include <xtables.h> +#include <linux/netfilter/xt_connlimit.h> + +static void connlimit_help(void) +{ + printf( +"connlimit match options:\n" +"[!] --connlimit-above n match if the number of existing " +" connections is (not) above n\n" +" --connlimit-mask n group hosts using mask\n"); +} + +static const struct option connlimit_opts[] = { + {"connlimit-above", 1, NULL, 'A'}, + {"connlimit-mask", 1, NULL, 'M'}, + { .name = NULL } +}; + +static void connlimit_init(struct xt_entry_match *match) +{ + struct xt_connlimit_info *info = (void *)match->data; + + /* This will also initialize the v4 mask correctly */ + memset(info->v6_mask, 0xFF, sizeof(info->v6_mask)); +} + +static void prefix_to_netmask(u_int32_t *mask, unsigned int prefix_len) +{ + if (prefix_len == 0) { + mask[0] = mask[1] = mask[2] = mask[3] = 0; + } else if (prefix_len <= 32) { + mask[0] <<= 32 - prefix_len; + mask[1] = mask[2] = mask[3] = 0; + } else if (prefix_len <= 64) { + mask[1] <<= 32 - (prefix_len - 32); + mask[2] = mask[3] = 0; + } else if (prefix_len <= 96) { + mask[2] <<= 32 - (prefix_len - 64); + mask[3] = 0; + } else if (prefix_len <= 128) { + mask[3] <<= 32 - (prefix_len - 96); + } + mask[0] = htonl(mask[0]); + mask[1] = htonl(mask[1]); + mask[2] = htonl(mask[2]); + mask[3] = htonl(mask[3]); +} + +static int connlimit_parse(int c, char **argv, int invert, unsigned int *flags, + struct xt_connlimit_info *info, unsigned int family) +{ + char *err; + int i; + + switch (c) { + case 'A': + if (*flags & 0x1) + xtables_error(PARAMETER_PROBLEM, + "--connlimit-above may be given only once"); + *flags |= 0x1; + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + info->limit = strtoul(optarg, NULL, 0); + info->inverse = invert; + break; + case 'M': + if (*flags & 0x2) + xtables_error(PARAMETER_PROBLEM, + "--connlimit-mask may be given only once"); + + *flags |= 0x2; + i = strtoul(optarg, &err, 0); + if (family == NFPROTO_IPV6) { + if (i > 128 || *err != '\0') + xtables_error(PARAMETER_PROBLEM, + "--connlimit-mask must be between " + "0 and 128"); + prefix_to_netmask(info->v6_mask, i); + } else { + if (i > 32 || *err != '\0') + xtables_error(PARAMETER_PROBLEM, + "--connlimit-mask must be between " + "0 and 32"); + if (i == 0) + info->v4_mask = 0; + else + info->v4_mask = htonl(0xFFFFFFFF << (32 - i)); + } + break; + default: + return 0; + } + + return 1; +} + +static int connlimit_parse4(int c, char **argv, int invert, + unsigned int *flags, const void *entry, + struct xt_entry_match **match) +{ + return connlimit_parse(c, argv, invert, flags, + (void *)(*match)->data, NFPROTO_IPV4); +} + +static int connlimit_parse6(int c, char **argv, int invert, + unsigned int *flags, const void *entry, + struct xt_entry_match **match) +{ + return connlimit_parse(c, argv, invert, flags, + (void *)(*match)->data, NFPROTO_IPV6); +} + +static void connlimit_check(unsigned int flags) +{ + if (!(flags & 0x1)) + xtables_error(PARAMETER_PROBLEM, + "You must specify \"--connlimit-above\""); +} + +static unsigned int count_bits4(u_int32_t mask) +{ + unsigned int bits = 0; + + for (mask = ~ntohl(mask); mask != 0; mask >>= 1) + ++bits; + + return 32 - bits; +} + +static unsigned int count_bits6(const u_int32_t *mask) +{ + unsigned int bits = 0, i; + u_int32_t tmp[4]; + + for (i = 0; i < 4; ++i) + for (tmp[i] = ~ntohl(mask[i]); tmp[i] != 0; tmp[i] >>= 1) + ++bits; + return 128 - bits; +} + +static void connlimit_print4(const void *ip, + const struct xt_entry_match *match, int numeric) +{ + const struct xt_connlimit_info *info = (const void *)match->data; + + printf("#conn/%u %s %u ", count_bits4(info->v4_mask), + info->inverse ? "<=" : ">", info->limit); +} + +static void connlimit_print6(const void *ip, + const struct xt_entry_match *match, int numeric) +{ + const struct xt_connlimit_info *info = (const void *)match->data; + printf("#conn/%u %s %u ", count_bits6(info->v6_mask), + info->inverse ? "<=" : ">", info->limit); +} + +static void connlimit_save4(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_connlimit_info *info = (const void *)match->data; + + printf("%s--connlimit-above %u --connlimit-mask %u ", + info->inverse ? "! " : "", info->limit, + count_bits4(info->v4_mask)); +} + +static void connlimit_save6(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_connlimit_info *info = (const void *)match->data; + + printf("%s--connlimit-above %u --connlimit-mask %u ", + info->inverse ? "! " : "", info->limit, + count_bits6(info->v6_mask)); +} + +static struct xtables_match connlimit_mt_reg[] = { + { + .name = "connlimit", + .family = NFPROTO_IPV4, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_connlimit_info)), + .userspacesize = offsetof(struct xt_connlimit_info, data), + .help = connlimit_help, + .init = connlimit_init, + .parse = connlimit_parse4, + .final_check = connlimit_check, + .print = connlimit_print4, + .save = connlimit_save4, + .extra_opts = connlimit_opts, + }, + { + .name = "connlimit", + .family = NFPROTO_IPV6, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_connlimit_info)), + .userspacesize = offsetof(struct xt_connlimit_info, data), + .help = connlimit_help, + .init = connlimit_init, + .parse = connlimit_parse6, + .final_check = connlimit_check, + .print = connlimit_print6, + .save = connlimit_save6, + .extra_opts = connlimit_opts, + }, +}; + +void _init(void) +{ + xtables_register_matches(connlimit_mt_reg, ARRAY_SIZE(connlimit_mt_reg)); +} diff --git a/extensions/libxt_connlimit.man b/extensions/libxt_connlimit.man new file mode 100644 index 0000000..c85d768 --- /dev/null +++ b/extensions/libxt_connlimit.man @@ -0,0 +1,27 @@ +Allows you to restrict the number of parallel connections to a server per +client IP address (or client address block). +.TP +[\fB!\fP] \fB\-\-connlimit\-above\fP \fIn\fP +Match if the number of existing connections is (not) above \fIn\fR. +.TP +\fB\-\-connlimit\-mask\fP \fIprefix_length\fP +Group hosts using the prefix length. For IPv4, this must be a number between +(including) 0 and 32. For IPv6, between 0 and 128. +.P +Examples: +.TP +# allow 2 telnet connections per client host +iptables \-A INPUT \-p tcp \-\-syn \-\-dport 23 \-m connlimit \-\-connlimit\-above 2 \-j REJECT +.TP +# you can also match the other way around: +iptables \-A INPUT \-p tcp \-\-syn \-\-dport 23 \-m connlimit ! \-\-connlimit\-above 2 \-j ACCEPT +.TP +# limit the number of parallel HTTP requests to 16 per class C sized \ +network (24 bit netmask) +iptables \-p tcp \-\-syn \-\-dport 80 \-m connlimit \-\-connlimit\-above 16 +\-\-connlimit\-mask 24 \-j REJECT +.TP +# limit the number of parallel HTTP requests to 16 for the link local network +(ipv6) +ip6tables \-p tcp \-\-syn \-\-dport 80 \-s fe80::/64 \-m connlimit \-\-connlimit\-above +16 \-\-connlimit\-mask 64 \-j REJECT diff --git a/extensions/libxt_connmark.c b/extensions/libxt_connmark.c new file mode 100644 index 0000000..38aa563 --- /dev/null +++ b/extensions/libxt_connmark.c @@ -0,0 +1,205 @@ +/* Shared library add-on to iptables to add connmark matching support. + * + * (C) 2002,2004 MARA Systems AB <http://www.marasystems.com> + * by Henrik Nordstrom <hno@marasystems.com> + * + * Version 1.1 + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/xt_connmark.h> + +struct xt_connmark_info { + unsigned long mark, mask; + u_int8_t invert; +}; + +enum { + F_MARK = 1 << 0, +}; + +static void connmark_mt_help(void) +{ + printf( +"connmark match options:\n" +"[!] --mark value[/mask] Match ctmark value with optional mask\n"); +} + +static const struct option connmark_mt_opts[] = { + {.name = "mark", .has_arg = true, .val = '1'}, + { .name = NULL } +}; + +static int +connmark_mt_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_connmark_mtinfo1 *info = (void *)(*match)->data; + unsigned int mark, mask = UINT32_MAX; + char *end; + + switch (c) { + case '1': /* --mark */ + xtables_param_act(XTF_ONLY_ONCE, "connmark", "--mark", *flags & F_MARK); + if (!xtables_strtoui(optarg, &end, &mark, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "connmark", "--mark", optarg); + if (*end == '/') + if (!xtables_strtoui(end + 1, &end, &mask, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "connmark", "--mark", optarg); + if (*end != '\0') + xtables_param_act(XTF_BAD_VALUE, "connmark", "--mark", optarg); + + if (invert) + info->invert = true; + info->mark = mark; + info->mask = mask; + *flags |= F_MARK; + return true; + } + return false; +} + +static int +connmark_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_connmark_info *markinfo = (struct xt_connmark_info *)(*match)->data; + + switch (c) { + char *end; + case '1': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + markinfo->mark = strtoul(optarg, &end, 0); + markinfo->mask = 0xffffffffUL; + + if (*end == '/') + markinfo->mask = strtoul(end+1, &end, 0); + + if (*end != '\0' || end == optarg) + xtables_error(PARAMETER_PROBLEM, "Bad MARK value \"%s\"", optarg); + if (invert) + markinfo->invert = 1; + *flags = 1; + break; + + default: + return 0; + } + return 1; +} + +static void print_mark(unsigned int mark, unsigned int mask) +{ + if (mask != 0xffffffffU) + printf("0x%x/0x%x ", mark, mask); + else + printf("0x%x ", mark); +} + +static void connmark_mt_check(unsigned int flags) +{ + if (flags == 0) + xtables_error(PARAMETER_PROBLEM, + "connmark: The --mark option is required"); +} + +static void +connmark_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_connmark_info *info = (const void *)match->data; + + printf("CONNMARK match "); + if (info->invert) + printf("!"); + print_mark(info->mark, info->mask); +} + +static void +connmark_mt_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_connmark_mtinfo1 *info = (const void *)match->data; + + printf("connmark match "); + if (info->invert) + printf("!"); + print_mark(info->mark, info->mask); +} + +static void connmark_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_connmark_info *info = (const void *)match->data; + + if (info->invert) + printf("! "); + + printf("--mark "); + print_mark(info->mark, info->mask); +} + +static void +connmark_mt_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_connmark_mtinfo1 *info = (const void *)match->data; + + if (info->invert) + printf("! "); + + printf("--mark "); + print_mark(info->mark, info->mask); +} + +static struct xtables_match connmark_mt_reg[] = { + { + .family = NFPROTO_UNSPEC, + .name = "connmark", + .revision = 0, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_connmark_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_connmark_info)), + .help = connmark_mt_help, + .parse = connmark_parse, + .final_check = connmark_mt_check, + .print = connmark_print, + .save = connmark_save, + .extra_opts = connmark_mt_opts, + }, + { + .version = XTABLES_VERSION, + .name = "connmark", + .revision = 1, + .family = NFPROTO_UNSPEC, + .size = XT_ALIGN(sizeof(struct xt_connmark_mtinfo1)), + .userspacesize = XT_ALIGN(sizeof(struct xt_connmark_mtinfo1)), + .help = connmark_mt_help, + .parse = connmark_mt_parse, + .final_check = connmark_mt_check, + .print = connmark_mt_print, + .save = connmark_mt_save, + .extra_opts = connmark_mt_opts, + }, +}; + +void _init(void) +{ + xtables_register_matches(connmark_mt_reg, ARRAY_SIZE(connmark_mt_reg)); +} diff --git a/extensions/libxt_connmark.man b/extensions/libxt_connmark.man new file mode 100644 index 0000000..ee87d9e --- /dev/null +++ b/extensions/libxt_connmark.man @@ -0,0 +1,6 @@ +This module matches the netfilter mark field associated with a connection +(which can be set using the \fBCONNMARK\fR target below). +.TP +[\fB!\fP] \fB\-\-mark\fP \fIvalue\fP[\fB/\fP\fImask\fP] +Matches packets in connections with the given mark value (if a mask is +specified, this is logically ANDed with the mark before the comparison). diff --git a/extensions/libxt_conntrack.c b/extensions/libxt_conntrack.c new file mode 100644 index 0000000..5557d3e --- /dev/null +++ b/extensions/libxt_conntrack.c @@ -0,0 +1,1243 @@ +/* + * libxt_conntrack + * Shared library add-on to iptables for conntrack matching support. + * + * GPL (C) 2001 Marc Boucher (marc@mbsi.ca). + * Copyright © CC Computer Consultants GmbH, 2007 - 2008 + * Jan Engelhardt <jengelh@computergmbh.de> + */ +#include <sys/socket.h> +#include <sys/types.h> +#include <ctype.h> +#include <getopt.h> +#include <netdb.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <xtables.h> +#include <linux/netfilter.h> +#include <linux/netfilter/xt_conntrack.h> +#include <linux/netfilter/nf_conntrack_common.h> +#include <arpa/inet.h> + +struct ip_conntrack_old_tuple { + struct { + __be32 ip; + union { + __u16 all; + } u; + } src; + + struct { + __be32 ip; + union { + __u16 all; + } u; + + /* The protocol. */ + __u16 protonum; + } dst; +}; + +struct xt_conntrack_info { + unsigned int statemask, statusmask; + + struct ip_conntrack_old_tuple tuple[IP_CT_DIR_MAX]; + struct in_addr sipmsk[IP_CT_DIR_MAX], dipmsk[IP_CT_DIR_MAX]; + + unsigned long expires_min, expires_max; + + /* Flags word */ + u_int8_t flags; + /* Inverse flags */ + u_int8_t invflags; +}; + +static void conntrack_mt_help(void) +{ + printf( +"conntrack match options:\n" +"[!] --ctstate {INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|SNAT|DNAT}[,...]\n" +" State(s) to match\n" +"[!] --ctproto proto Protocol to match; by number or name, e.g. \"tcp\"\n" +"[!] --ctorigsrc address[/mask]\n" +"[!] --ctorigdst address[/mask]\n" +"[!] --ctreplsrc address[/mask]\n" +"[!] --ctrepldst address[/mask]\n" +" Original/Reply source/destination address\n" +"[!] --ctorigsrcport port\n" +"[!] --ctorigdstport port\n" +"[!] --ctreplsrcport port\n" +"[!] --ctrepldstport port\n" +" TCP/UDP/SCTP orig./reply source/destination port\n" +"[!] --ctstatus {NONE|EXPECTED|SEEN_REPLY|ASSURED|CONFIRMED}[,...]\n" +" Status(es) to match\n" +"[!] --ctexpire time[:time] Match remaining lifetime in seconds against\n" +" value or range of values (inclusive)\n" +" --ctdir {ORIGINAL|REPLY} Flow direction of packet\n"); +} + +static const struct option conntrack_mt_opts_v0[] = { + {.name = "ctstate", .has_arg = true, .val = '1'}, + {.name = "ctproto", .has_arg = true, .val = '2'}, + {.name = "ctorigsrc", .has_arg = true, .val = '3'}, + {.name = "ctorigdst", .has_arg = true, .val = '4'}, + {.name = "ctreplsrc", .has_arg = true, .val = '5'}, + {.name = "ctrepldst", .has_arg = true, .val = '6'}, + {.name = "ctstatus", .has_arg = true, .val = '7'}, + {.name = "ctexpire", .has_arg = true, .val = '8'}, + { .name = NULL } +}; + +static const struct option conntrack_mt_opts[] = { + {.name = "ctstate", .has_arg = true, .val = '1'}, + {.name = "ctproto", .has_arg = true, .val = '2'}, + {.name = "ctorigsrc", .has_arg = true, .val = '3'}, + {.name = "ctorigdst", .has_arg = true, .val = '4'}, + {.name = "ctreplsrc", .has_arg = true, .val = '5'}, + {.name = "ctrepldst", .has_arg = true, .val = '6'}, + {.name = "ctstatus", .has_arg = true, .val = '7'}, + {.name = "ctexpire", .has_arg = true, .val = '8'}, + {.name = "ctorigsrcport", .has_arg = true, .val = 'a'}, + {.name = "ctorigdstport", .has_arg = true, .val = 'b'}, + {.name = "ctreplsrcport", .has_arg = true, .val = 'c'}, + {.name = "ctrepldstport", .has_arg = true, .val = 'd'}, + {.name = "ctdir", .has_arg = true, .val = 'e'}, + {.name = NULL}, +}; + +static int +parse_state(const char *state, size_t len, struct xt_conntrack_info *sinfo) +{ + if (strncasecmp(state, "INVALID", len) == 0) + sinfo->statemask |= XT_CONNTRACK_STATE_INVALID; + else if (strncasecmp(state, "NEW", len) == 0) + sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW); + else if (strncasecmp(state, "ESTABLISHED", len) == 0) + sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED); + else if (strncasecmp(state, "RELATED", len) == 0) + sinfo->statemask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED); + else if (strncasecmp(state, "UNTRACKED", len) == 0) + sinfo->statemask |= XT_CONNTRACK_STATE_UNTRACKED; + else if (strncasecmp(state, "SNAT", len) == 0) + sinfo->statemask |= XT_CONNTRACK_STATE_SNAT; + else if (strncasecmp(state, "DNAT", len) == 0) + sinfo->statemask |= XT_CONNTRACK_STATE_DNAT; + else + return 0; + return 1; +} + +static void +parse_states(const char *arg, struct xt_conntrack_info *sinfo) +{ + const char *comma; + + while ((comma = strchr(arg, ',')) != NULL) { + if (comma == arg || !parse_state(arg, comma-arg, sinfo)) + xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg); + arg = comma+1; + } + if (!*arg) + xtables_error(PARAMETER_PROBLEM, "\"--ctstate\" requires a list of " + "states with no spaces, e.g. " + "ESTABLISHED,RELATED"); + if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo)) + xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg); +} + +static bool +conntrack_ps_state(struct xt_conntrack_mtinfo2 *info, const char *state, + size_t z) +{ + if (strncasecmp(state, "INVALID", z) == 0) + info->state_mask |= XT_CONNTRACK_STATE_INVALID; + else if (strncasecmp(state, "NEW", z) == 0) + info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_NEW); + else if (strncasecmp(state, "ESTABLISHED", z) == 0) + info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED); + else if (strncasecmp(state, "RELATED", z) == 0) + info->state_mask |= XT_CONNTRACK_STATE_BIT(IP_CT_RELATED); + else if (strncasecmp(state, "UNTRACKED", z) == 0) + info->state_mask |= XT_CONNTRACK_STATE_UNTRACKED; + else if (strncasecmp(state, "SNAT", z) == 0) + info->state_mask |= XT_CONNTRACK_STATE_SNAT; + else if (strncasecmp(state, "DNAT", z) == 0) + info->state_mask |= XT_CONNTRACK_STATE_DNAT; + else + return false; + return true; +} + +static void +conntrack_ps_states(struct xt_conntrack_mtinfo2 *info, const char *arg) +{ + const char *comma; + + while ((comma = strchr(arg, ',')) != NULL) { + if (comma == arg || !conntrack_ps_state(info, arg, comma - arg)) + xtables_error(PARAMETER_PROBLEM, + "Bad ctstate \"%s\"", arg); + arg = comma + 1; + } + + if (strlen(arg) == 0 || !conntrack_ps_state(info, arg, strlen(arg))) + xtables_error(PARAMETER_PROBLEM, "Bad ctstate \"%s\"", arg); +} + +static int +parse_status(const char *status, size_t len, struct xt_conntrack_info *sinfo) +{ + if (strncasecmp(status, "NONE", len) == 0) + sinfo->statusmask |= 0; + else if (strncasecmp(status, "EXPECTED", len) == 0) + sinfo->statusmask |= IPS_EXPECTED; + else if (strncasecmp(status, "SEEN_REPLY", len) == 0) + sinfo->statusmask |= IPS_SEEN_REPLY; + else if (strncasecmp(status, "ASSURED", len) == 0) + sinfo->statusmask |= IPS_ASSURED; +#ifdef IPS_CONFIRMED + else if (strncasecmp(status, "CONFIRMED", len) == 0) + sinfo->statusmask |= IPS_CONFIRMED; +#endif + else + return 0; + return 1; +} + +static void +parse_statuses(const char *arg, struct xt_conntrack_info *sinfo) +{ + const char *comma; + + while ((comma = strchr(arg, ',')) != NULL) { + if (comma == arg || !parse_status(arg, comma-arg, sinfo)) + xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg); + arg = comma+1; + } + + if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo)) + xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg); +} + +static bool +conntrack_ps_status(struct xt_conntrack_mtinfo2 *info, const char *status, + size_t z) +{ + if (strncasecmp(status, "NONE", z) == 0) + info->status_mask |= 0; + else if (strncasecmp(status, "EXPECTED", z) == 0) + info->status_mask |= IPS_EXPECTED; + else if (strncasecmp(status, "SEEN_REPLY", z) == 0) + info->status_mask |= IPS_SEEN_REPLY; + else if (strncasecmp(status, "ASSURED", z) == 0) + info->status_mask |= IPS_ASSURED; + else if (strncasecmp(status, "CONFIRMED", z) == 0) + info->status_mask |= IPS_CONFIRMED; + else + return false; + return true; +} + +static void +conntrack_ps_statuses(struct xt_conntrack_mtinfo2 *info, const char *arg) +{ + const char *comma; + + while ((comma = strchr(arg, ',')) != NULL) { + if (comma == arg || !conntrack_ps_status(info, arg, comma - arg)) + xtables_error(PARAMETER_PROBLEM, + "Bad ctstatus \"%s\"", arg); + arg = comma + 1; + } + + if (strlen(arg) == 0 || !conntrack_ps_status(info, arg, strlen(arg))) + xtables_error(PARAMETER_PROBLEM, "Bad ctstatus \"%s\"", arg); +} + +static unsigned long +parse_expire(const char *s) +{ + unsigned int len; + + if (!xtables_strtoui(s, NULL, &len, 0, UINT32_MAX)) + xtables_error(PARAMETER_PROBLEM, "expire value invalid: \"%s\"\n", s); + else + return len; +} + +/* If a single value is provided, min and max are both set to the value */ +static void +parse_expires(const char *s, struct xt_conntrack_info *sinfo) +{ + char *buffer; + char *cp; + + buffer = strdup(s); + if ((cp = strchr(buffer, ':')) == NULL) + sinfo->expires_min = sinfo->expires_max = + parse_expire(buffer); + else { + *cp = '\0'; + cp++; + + sinfo->expires_min = buffer[0] ? parse_expire(buffer) : 0; + sinfo->expires_max = cp[0] + ? parse_expire(cp) + : (unsigned long)-1; + } + free(buffer); + + if (sinfo->expires_min > sinfo->expires_max) + xtables_error(PARAMETER_PROBLEM, + "expire min. range value `%lu' greater than max. " + "range value `%lu'", sinfo->expires_min, sinfo->expires_max); +} + +static void +conntrack_ps_expires(struct xt_conntrack_mtinfo2 *info, const char *s) +{ + unsigned int min, max; + char *end; + + if (!xtables_strtoui(s, &end, &min, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "conntrack", "--expires", s); + max = min; + if (*end == ':') + if (!xtables_strtoui(end + 1, &end, &max, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "conntrack", "--expires", s); + if (*end != '\0') + xtables_param_act(XTF_BAD_VALUE, "conntrack", "--expires", s); + + if (min > max) + xtables_error(PARAMETER_PROBLEM, + "expire min. range value \"%u\" greater than max. " + "range value \"%u\"", min, max); + + info->expires_min = min; + info->expires_max = max; +} + +static int conntrack_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_conntrack_info *sinfo = (void *)(*match)->data; + char *protocol = NULL; + unsigned int naddrs = 0; + struct in_addr *addrs = NULL; + + + switch (c) { + case '1': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + parse_states(optarg, sinfo); + if (invert) { + sinfo->invflags |= XT_CONNTRACK_STATE; + } + sinfo->flags |= XT_CONNTRACK_STATE; + break; + + case '2': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + if(invert) + sinfo->invflags |= XT_CONNTRACK_PROTO; + + /* Canonicalize into lower case */ + for (protocol = optarg; *protocol; protocol++) + *protocol = tolower(*protocol); + + protocol = optarg; + sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = + xtables_parse_protocol(protocol); + + if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0 + && (sinfo->invflags & XT_INV_PROTO)) + xtables_error(PARAMETER_PROBLEM, + "rule would never match protocol"); + + sinfo->flags |= XT_CONNTRACK_PROTO; + break; + + case '3': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + if (invert) + sinfo->invflags |= XT_CONNTRACK_ORIGSRC; + + xtables_ipparse_any(optarg, &addrs, + &sinfo->sipmsk[IP_CT_DIR_ORIGINAL], + &naddrs); + if(naddrs > 1) + xtables_error(PARAMETER_PROBLEM, + "multiple IP addresses not allowed"); + + if(naddrs == 1) { + sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = addrs[0].s_addr; + } + + sinfo->flags |= XT_CONNTRACK_ORIGSRC; + break; + + case '4': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + if (invert) + sinfo->invflags |= XT_CONNTRACK_ORIGDST; + + xtables_ipparse_any(optarg, &addrs, + &sinfo->dipmsk[IP_CT_DIR_ORIGINAL], + &naddrs); + if(naddrs > 1) + xtables_error(PARAMETER_PROBLEM, + "multiple IP addresses not allowed"); + + if(naddrs == 1) { + sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = addrs[0].s_addr; + } + + sinfo->flags |= XT_CONNTRACK_ORIGDST; + break; + + case '5': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + if (invert) + sinfo->invflags |= XT_CONNTRACK_REPLSRC; + + xtables_ipparse_any(optarg, &addrs, + &sinfo->sipmsk[IP_CT_DIR_REPLY], + &naddrs); + if(naddrs > 1) + xtables_error(PARAMETER_PROBLEM, + "multiple IP addresses not allowed"); + + if(naddrs == 1) { + sinfo->tuple[IP_CT_DIR_REPLY].src.ip = addrs[0].s_addr; + } + + sinfo->flags |= XT_CONNTRACK_REPLSRC; + break; + + case '6': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + if (invert) + sinfo->invflags |= XT_CONNTRACK_REPLDST; + + xtables_ipparse_any(optarg, &addrs, + &sinfo->dipmsk[IP_CT_DIR_REPLY], + &naddrs); + if(naddrs > 1) + xtables_error(PARAMETER_PROBLEM, + "multiple IP addresses not allowed"); + + if(naddrs == 1) { + sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = addrs[0].s_addr; + } + + sinfo->flags |= XT_CONNTRACK_REPLDST; + break; + + case '7': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + parse_statuses(optarg, sinfo); + if (invert) { + sinfo->invflags |= XT_CONNTRACK_STATUS; + } + sinfo->flags |= XT_CONNTRACK_STATUS; + break; + + case '8': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + parse_expires(optarg, sinfo); + if (invert) { + sinfo->invflags |= XT_CONNTRACK_EXPIRES; + } + sinfo->flags |= XT_CONNTRACK_EXPIRES; + break; + + default: + return 0; + } + + *flags = sinfo->flags; + return 1; +} + +static int +conntrack_mt_parse(int c, bool invert, unsigned int *flags, + struct xt_conntrack_mtinfo2 *info) +{ + unsigned int port; + char *p; + + switch (c) { + case '1': /* --ctstate */ + conntrack_ps_states(info, optarg); + info->match_flags |= XT_CONNTRACK_STATE; + if (invert) + info->invert_flags |= XT_CONNTRACK_STATE; + break; + + case '2': /* --ctproto */ + /* Canonicalize into lower case */ + for (p = optarg; *p != '\0'; ++p) + *p = tolower(*p); + info->l4proto = xtables_parse_protocol(optarg); + + if (info->l4proto == 0 && (info->invert_flags & XT_INV_PROTO)) + xtables_error(PARAMETER_PROBLEM, "conntrack: rule would " + "never match protocol"); + + info->match_flags |= XT_CONNTRACK_PROTO; + if (invert) + info->invert_flags |= XT_CONNTRACK_PROTO; + break; + + case '7': /* --ctstatus */ + conntrack_ps_statuses(info, optarg); + info->match_flags |= XT_CONNTRACK_STATUS; + if (invert) + info->invert_flags |= XT_CONNTRACK_STATUS; + break; + + case '8': /* --ctexpire */ + conntrack_ps_expires(info, optarg); + info->match_flags |= XT_CONNTRACK_EXPIRES; + if (invert) + info->invert_flags |= XT_CONNTRACK_EXPIRES; + break; + + case 'a': /* --ctorigsrcport */ + if (!xtables_strtoui(optarg, NULL, &port, 0, UINT16_MAX)) + xtables_param_act(XTF_BAD_VALUE, "conntrack", + "--ctorigsrcport", optarg); + info->match_flags |= XT_CONNTRACK_ORIGSRC_PORT; + info->origsrc_port = htons(port); + if (invert) + info->invert_flags |= XT_CONNTRACK_ORIGSRC_PORT; + break; + + case 'b': /* --ctorigdstport */ + if (!xtables_strtoui(optarg, NULL, &port, 0, UINT16_MAX)) + xtables_param_act(XTF_BAD_VALUE, "conntrack", + "--ctorigdstport", optarg); + info->match_flags |= XT_CONNTRACK_ORIGDST_PORT; + info->origdst_port = htons(port); + if (invert) + info->invert_flags |= XT_CONNTRACK_ORIGDST_PORT; + break; + + case 'c': /* --ctreplsrcport */ + if (!xtables_strtoui(optarg, NULL, &port, 0, UINT16_MAX)) + xtables_param_act(XTF_BAD_VALUE, "conntrack", + "--ctreplsrcport", optarg); + info->match_flags |= XT_CONNTRACK_REPLSRC_PORT; + info->replsrc_port = htons(port); + if (invert) + info->invert_flags |= XT_CONNTRACK_REPLSRC_PORT; + break; + + case 'd': /* --ctrepldstport */ + if (!xtables_strtoui(optarg, NULL, &port, 0, UINT16_MAX)) + xtables_param_act(XTF_BAD_VALUE, "conntrack", + "--ctrepldstport", optarg); + info->match_flags |= XT_CONNTRACK_REPLDST_PORT; + info->repldst_port = htons(port); + if (invert) + info->invert_flags |= XT_CONNTRACK_REPLDST_PORT; + break; + + case 'e': /* --ctdir */ + xtables_param_act(XTF_NO_INVERT, "conntrack", "--ctdir", invert); + if (strcasecmp(optarg, "ORIGINAL") == 0) { + info->match_flags |= XT_CONNTRACK_DIRECTION; + info->invert_flags &= ~XT_CONNTRACK_DIRECTION; + } else if (strcasecmp(optarg, "REPLY") == 0) { + info->match_flags |= XT_CONNTRACK_DIRECTION; + info->invert_flags |= XT_CONNTRACK_DIRECTION; + } else { + xtables_param_act(XTF_BAD_VALUE, "conntrack", "--ctdir", optarg); + } + break; + + default: + return false; + } + + *flags = info->match_flags; + return true; +} + +static int +conntrack_mt4_parse(int c, bool invert, unsigned int *flags, + struct xt_conntrack_mtinfo2 *info) +{ + struct in_addr *addr = NULL; + unsigned int naddrs = 0; + + switch (c) { + case '3': /* --ctorigsrc */ + xtables_ipparse_any(optarg, &addr, &info->origsrc_mask.in, + &naddrs); + if (naddrs > 1) + xtables_error(PARAMETER_PROBLEM, + "multiple IP addresses not allowed"); + if (naddrs == 1) + memcpy(&info->origsrc_addr.in, addr, sizeof(*addr)); + info->match_flags |= XT_CONNTRACK_ORIGSRC; + if (invert) + info->invert_flags |= XT_CONNTRACK_ORIGSRC; + break; + + case '4': /* --ctorigdst */ + xtables_ipparse_any(optarg, &addr, &info->origdst_mask.in, + &naddrs); + if (naddrs > 1) + xtables_error(PARAMETER_PROBLEM, + "multiple IP addresses not allowed"); + if (naddrs == 1) + memcpy(&info->origdst_addr.in, addr, sizeof(*addr)); + info->match_flags |= XT_CONNTRACK_ORIGDST; + if (invert) + info->invert_flags |= XT_CONNTRACK_ORIGDST; + break; + + case '5': /* --ctreplsrc */ + xtables_ipparse_any(optarg, &addr, &info->replsrc_mask.in, + &naddrs); + if (naddrs > 1) + xtables_error(PARAMETER_PROBLEM, + "multiple IP addresses not allowed"); + if (naddrs == 1) + memcpy(&info->replsrc_addr.in, addr, sizeof(*addr)); + info->match_flags |= XT_CONNTRACK_REPLSRC; + if (invert) + info->invert_flags |= XT_CONNTRACK_REPLSRC; + break; + + case '6': /* --ctrepldst */ + xtables_ipparse_any(optarg, &addr, &info->repldst_mask.in, + &naddrs); + if (naddrs > 1) + xtables_error(PARAMETER_PROBLEM, + "multiple IP addresses not allowed"); + if (naddrs == 1) + memcpy(&info->repldst_addr.in, addr, sizeof(*addr)); + info->match_flags |= XT_CONNTRACK_REPLDST; + if (invert) + info->invert_flags |= XT_CONNTRACK_REPLDST; + break; + + + default: + return conntrack_mt_parse(c, invert, flags, info); + } + + *flags = info->match_flags; + return true; +} + +static int +conntrack_mt6_parse(int c, bool invert, unsigned int *flags, + struct xt_conntrack_mtinfo2 *info) +{ + struct in6_addr *addr = NULL; + unsigned int naddrs = 0; + + switch (c) { + case '3': /* --ctorigsrc */ + xtables_ip6parse_any(optarg, &addr, + &info->origsrc_mask.in6, &naddrs); + if (naddrs > 1) + xtables_error(PARAMETER_PROBLEM, + "multiple IP addresses not allowed"); + if (naddrs == 1) + memcpy(&info->origsrc_addr.in6, addr, sizeof(*addr)); + info->match_flags |= XT_CONNTRACK_ORIGSRC; + if (invert) + info->invert_flags |= XT_CONNTRACK_ORIGSRC; + break; + + case '4': /* --ctorigdst */ + xtables_ip6parse_any(optarg, &addr, + &info->origdst_mask.in6, &naddrs); + if (naddrs > 1) + xtables_error(PARAMETER_PROBLEM, + "multiple IP addresses not allowed"); + if (naddrs == 1) + memcpy(&info->origdst_addr.in, addr, sizeof(*addr)); + info->match_flags |= XT_CONNTRACK_ORIGDST; + if (invert) + info->invert_flags |= XT_CONNTRACK_ORIGDST; + break; + + case '5': /* --ctreplsrc */ + xtables_ip6parse_any(optarg, &addr, + &info->replsrc_mask.in6, &naddrs); + if (naddrs > 1) + xtables_error(PARAMETER_PROBLEM, + "multiple IP addresses not allowed"); + if (naddrs == 1) + memcpy(&info->replsrc_addr.in, addr, sizeof(*addr)); + info->match_flags |= XT_CONNTRACK_REPLSRC; + if (invert) + info->invert_flags |= XT_CONNTRACK_REPLSRC; + break; + + case '6': /* --ctrepldst */ + xtables_ip6parse_any(optarg, &addr, + &info->repldst_mask.in6, &naddrs); + if (naddrs > 1) + xtables_error(PARAMETER_PROBLEM, + "multiple IP addresses not allowed"); + if (naddrs == 1) + memcpy(&info->repldst_addr.in, addr, sizeof(*addr)); + info->match_flags |= XT_CONNTRACK_REPLDST; + if (invert) + info->invert_flags |= XT_CONNTRACK_REPLDST; + break; + + + default: + return conntrack_mt_parse(c, invert, flags, info); + } + + *flags = info->match_flags; + return true; +} + +#define cinfo_transform(r, l) \ + do { \ + memcpy((r), (l), offsetof(typeof(*(l)), state_mask)); \ + (r)->state_mask = (l)->state_mask; \ + (r)->status_mask = (l)->status_mask; \ + } while (false); + +static int +conntrack1_mt4_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_conntrack_mtinfo1 *info = (void *)(*match)->data; + struct xt_conntrack_mtinfo2 up; + + cinfo_transform(&up, info); + if (!conntrack_mt4_parse(c, invert, flags, &up)) + return false; + cinfo_transform(info, &up); + return true; +} + +static int +conntrack1_mt6_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_conntrack_mtinfo1 *info = (void *)(*match)->data; + struct xt_conntrack_mtinfo2 up; + + cinfo_transform(&up, info); + if (!conntrack_mt6_parse(c, invert, flags, &up)) + return false; + cinfo_transform(info, &up); + return true; +} + +static int +conntrack2_mt4_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + return conntrack_mt4_parse(c, invert, flags, (void *)(*match)->data); +} + +static int +conntrack2_mt6_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + return conntrack_mt6_parse(c, invert, flags, (void *)(*match)->data); +} + +static void conntrack_mt_check(unsigned int flags) +{ + if (flags == 0) + xtables_error(PARAMETER_PROBLEM, "conntrack: At least one option " + "is required"); +} + +static void +print_state(unsigned int statemask) +{ + const char *sep = ""; + + if (statemask & XT_CONNTRACK_STATE_INVALID) { + printf("%sINVALID", sep); + sep = ","; + } + if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_NEW)) { + printf("%sNEW", sep); + sep = ","; + } + if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) { + printf("%sRELATED", sep); + sep = ","; + } + if (statemask & XT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) { + printf("%sESTABLISHED", sep); + sep = ","; + } + if (statemask & XT_CONNTRACK_STATE_UNTRACKED) { + printf("%sUNTRACKED", sep); + sep = ","; + } + if (statemask & XT_CONNTRACK_STATE_SNAT) { + printf("%sSNAT", sep); + sep = ","; + } + if (statemask & XT_CONNTRACK_STATE_DNAT) { + printf("%sDNAT", sep); + sep = ","; + } + printf(" "); +} + +static void +print_status(unsigned int statusmask) +{ + const char *sep = ""; + + if (statusmask & IPS_EXPECTED) { + printf("%sEXPECTED", sep); + sep = ","; + } + if (statusmask & IPS_SEEN_REPLY) { + printf("%sSEEN_REPLY", sep); + sep = ","; + } + if (statusmask & IPS_ASSURED) { + printf("%sASSURED", sep); + sep = ","; + } + if (statusmask & IPS_CONFIRMED) { + printf("%sCONFIRMED", sep); + sep = ","; + } + if (statusmask == 0) + printf("%sNONE", sep); + printf(" "); +} + +static void +conntrack_dump_addr(const union nf_inet_addr *addr, + const union nf_inet_addr *mask, + unsigned int family, bool numeric) +{ + if (family == NFPROTO_IPV4) { + if (!numeric && addr->ip == 0) { + printf("anywhere "); + return; + } + if (numeric) + printf("%s%s ", + xtables_ipaddr_to_numeric(&addr->in), + xtables_ipmask_to_numeric(&mask->in)); + else + printf("%s%s ", + xtables_ipaddr_to_anyname(&addr->in), + xtables_ipmask_to_numeric(&mask->in)); + } else if (family == NFPROTO_IPV6) { + if (!numeric && addr->ip6[0] == 0 && addr->ip6[1] == 0 && + addr->ip6[2] == 0 && addr->ip6[3] == 0) { + printf("anywhere "); + return; + } + if (numeric) + printf("%s%s ", + xtables_ip6addr_to_numeric(&addr->in6), + xtables_ip6mask_to_numeric(&mask->in6)); + else + printf("%s%s ", + xtables_ip6addr_to_anyname(&addr->in6), + xtables_ip6mask_to_numeric(&mask->in6)); + } +} + +static void +print_addr(const struct in_addr *addr, const struct in_addr *mask, + int inv, int numeric) +{ + char buf[BUFSIZ]; + + if (inv) + printf("! "); + + if (mask->s_addr == 0L && !numeric) + printf("%s ", "anywhere"); + else { + if (numeric) + strcpy(buf, xtables_ipaddr_to_numeric(addr)); + else + strcpy(buf, xtables_ipaddr_to_anyname(addr)); + strcat(buf, xtables_ipmask_to_numeric(mask)); + printf("%s ", buf); + } +} + +static void +matchinfo_print(const void *ip, const struct xt_entry_match *match, int numeric, const char *optpfx) +{ + const struct xt_conntrack_info *sinfo = (const void *)match->data; + + if(sinfo->flags & XT_CONNTRACK_STATE) { + if (sinfo->invflags & XT_CONNTRACK_STATE) + printf("! "); + printf("%sctstate ", optpfx); + print_state(sinfo->statemask); + } + + if(sinfo->flags & XT_CONNTRACK_PROTO) { + if (sinfo->invflags & XT_CONNTRACK_PROTO) + printf("! "); + printf("%sctproto ", optpfx); + printf("%u ", sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum); + } + + if(sinfo->flags & XT_CONNTRACK_ORIGSRC) { + if (sinfo->invflags & XT_CONNTRACK_ORIGSRC) + printf("! "); + printf("%sctorigsrc ", optpfx); + + print_addr( + (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip, + &sinfo->sipmsk[IP_CT_DIR_ORIGINAL], + false, + numeric); + } + + if(sinfo->flags & XT_CONNTRACK_ORIGDST) { + if (sinfo->invflags & XT_CONNTRACK_ORIGDST) + printf("! "); + printf("%sctorigdst ", optpfx); + + print_addr( + (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip, + &sinfo->dipmsk[IP_CT_DIR_ORIGINAL], + false, + numeric); + } + + if(sinfo->flags & XT_CONNTRACK_REPLSRC) { + if (sinfo->invflags & XT_CONNTRACK_REPLSRC) + printf("! "); + printf("%sctreplsrc ", optpfx); + + print_addr( + (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip, + &sinfo->sipmsk[IP_CT_DIR_REPLY], + false, + numeric); + } + + if(sinfo->flags & XT_CONNTRACK_REPLDST) { + if (sinfo->invflags & XT_CONNTRACK_REPLDST) + printf("! "); + printf("%sctrepldst ", optpfx); + + print_addr( + (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip, + &sinfo->dipmsk[IP_CT_DIR_REPLY], + false, + numeric); + } + + if(sinfo->flags & XT_CONNTRACK_STATUS) { + if (sinfo->invflags & XT_CONNTRACK_STATUS) + printf("! "); + printf("%sctstatus ", optpfx); + print_status(sinfo->statusmask); + } + + if(sinfo->flags & XT_CONNTRACK_EXPIRES) { + if (sinfo->invflags & XT_CONNTRACK_EXPIRES) + printf("! "); + printf("%sctexpire ", optpfx); + + if (sinfo->expires_max == sinfo->expires_min) + printf("%lu ", sinfo->expires_min); + else + printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max); + } + + if (sinfo->flags & XT_CONNTRACK_DIRECTION) { + if (sinfo->invflags & XT_CONNTRACK_DIRECTION) + printf("%sctdir REPLY", optpfx); + else + printf("%sctdir ORIGINAL", optpfx); + } + +} + +static void +conntrack_dump(const struct xt_conntrack_mtinfo2 *info, const char *prefix, + unsigned int family, bool numeric) +{ + if (info->match_flags & XT_CONNTRACK_STATE) { + if (info->invert_flags & XT_CONNTRACK_STATE) + printf("! "); + printf("%sctstate ", prefix); + print_state(info->state_mask); + } + + if (info->match_flags & XT_CONNTRACK_PROTO) { + if (info->invert_flags & XT_CONNTRACK_PROTO) + printf("! "); + printf("%sctproto %u ", prefix, info->l4proto); + } + + if (info->match_flags & XT_CONNTRACK_ORIGSRC) { + if (info->invert_flags & XT_CONNTRACK_ORIGSRC) + printf("! "); + printf("%sctorigsrc ", prefix); + conntrack_dump_addr(&info->origsrc_addr, &info->origsrc_mask, + family, numeric); + } + + if (info->match_flags & XT_CONNTRACK_ORIGDST) { + if (info->invert_flags & XT_CONNTRACK_ORIGDST) + printf("! "); + printf("%sctorigdst ", prefix); + conntrack_dump_addr(&info->origdst_addr, &info->origdst_mask, + family, numeric); + } + + if (info->match_flags & XT_CONNTRACK_REPLSRC) { + if (info->invert_flags & XT_CONNTRACK_REPLSRC) + printf("! "); + printf("%sctreplsrc ", prefix); + conntrack_dump_addr(&info->replsrc_addr, &info->replsrc_mask, + family, numeric); + } + + if (info->match_flags & XT_CONNTRACK_REPLDST) { + if (info->invert_flags & XT_CONNTRACK_REPLDST) + printf("! "); + printf("%sctrepldst ", prefix); + conntrack_dump_addr(&info->repldst_addr, &info->repldst_mask, + family, numeric); + } + + if (info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) { + if (info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT) + printf("! "); + printf("%sctorigsrcport %u ", prefix, + ntohs(info->origsrc_port)); + } + + if (info->match_flags & XT_CONNTRACK_ORIGDST_PORT) { + if (info->invert_flags & XT_CONNTRACK_ORIGDST_PORT) + printf("! "); + printf("%sctorigdstport %u ", prefix, + ntohs(info->origdst_port)); + } + + if (info->match_flags & XT_CONNTRACK_REPLSRC_PORT) { + if (info->invert_flags & XT_CONNTRACK_REPLSRC_PORT) + printf("! "); + printf("%sctreplsrcport %u ", prefix, + ntohs(info->replsrc_port)); + } + + if (info->match_flags & XT_CONNTRACK_REPLDST_PORT) { + if (info->invert_flags & XT_CONNTRACK_REPLDST_PORT) + printf("! "); + printf("%sctrepldstport %u ", prefix, + ntohs(info->repldst_port)); + } + + if (info->match_flags & XT_CONNTRACK_STATUS) { + if (info->invert_flags & XT_CONNTRACK_STATUS) + printf("! "); + printf("%sctstatus ", prefix); + print_status(info->status_mask); + } + + if (info->match_flags & XT_CONNTRACK_EXPIRES) { + if (info->invert_flags & XT_CONNTRACK_EXPIRES) + printf("! "); + printf("%sctexpire ", prefix); + + if (info->expires_max == info->expires_min) + printf("%u ", (unsigned int)info->expires_min); + else + printf("%u:%u ", (unsigned int)info->expires_min, + (unsigned int)info->expires_max); + } + + if (info->match_flags & XT_CONNTRACK_DIRECTION) { + if (info->invert_flags & XT_CONNTRACK_DIRECTION) + printf("%sctdir REPLY", prefix); + else + printf("%sctdir ORIGINAL", prefix); + } +} + +static void conntrack_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + matchinfo_print(ip, match, numeric, ""); +} + +static void +conntrack1_mt4_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_conntrack_mtinfo1 *info = (void *)match->data; + struct xt_conntrack_mtinfo2 up; + + cinfo_transform(&up, info); + conntrack_dump(&up, "", NFPROTO_IPV4, numeric); +} + +static void +conntrack1_mt6_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_conntrack_mtinfo1 *info = (void *)match->data; + struct xt_conntrack_mtinfo2 up; + + cinfo_transform(&up, info); + conntrack_dump(&up, "", NFPROTO_IPV6, numeric); +} + +static void +conntrack_mt_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + conntrack_dump((const void *)match->data, "", NFPROTO_IPV4, numeric); +} + +static void +conntrack_mt6_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + conntrack_dump((const void *)match->data, "", NFPROTO_IPV6, numeric); +} + +static void conntrack_save(const void *ip, const struct xt_entry_match *match) +{ + matchinfo_print(ip, match, 1, "--"); +} + +static void conntrack_mt_save(const void *ip, + const struct xt_entry_match *match) +{ + conntrack_dump((const void *)match->data, "--", NFPROTO_IPV4, true); +} + +static void conntrack_mt6_save(const void *ip, + const struct xt_entry_match *match) +{ + conntrack_dump((const void *)match->data, "--", NFPROTO_IPV6, true); +} + +static void +conntrack1_mt4_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_conntrack_mtinfo1 *info = (void *)match->data; + struct xt_conntrack_mtinfo2 up; + + cinfo_transform(&up, info); + conntrack_dump(&up, "--", NFPROTO_IPV4, true); +} + +static void +conntrack1_mt6_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_conntrack_mtinfo1 *info = (void *)match->data; + struct xt_conntrack_mtinfo2 up; + + cinfo_transform(&up, info); + conntrack_dump(&up, "--", NFPROTO_IPV6, true); +} + +static struct xtables_match conntrack_mt_reg[] = { + { + .version = XTABLES_VERSION, + .name = "conntrack", + .revision = 0, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct xt_conntrack_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_info)), + .help = conntrack_mt_help, + .parse = conntrack_parse, + .final_check = conntrack_mt_check, + .print = conntrack_print, + .save = conntrack_save, + .extra_opts = conntrack_mt_opts_v0, + }, + { + .version = XTABLES_VERSION, + .name = "conntrack", + .revision = 1, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)), + .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)), + .help = conntrack_mt_help, + .parse = conntrack1_mt4_parse, + .final_check = conntrack_mt_check, + .print = conntrack1_mt4_print, + .save = conntrack1_mt4_save, + .extra_opts = conntrack_mt_opts, + }, + { + .version = XTABLES_VERSION, + .name = "conntrack", + .revision = 1, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)), + .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo1)), + .help = conntrack_mt_help, + .parse = conntrack1_mt6_parse, + .final_check = conntrack_mt_check, + .print = conntrack1_mt6_print, + .save = conntrack1_mt6_save, + .extra_opts = conntrack_mt_opts, + }, + { + .version = XTABLES_VERSION, + .name = "conntrack", + .revision = 2, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)), + .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)), + .help = conntrack_mt_help, + .parse = conntrack2_mt4_parse, + .final_check = conntrack_mt_check, + .print = conntrack_mt_print, + .save = conntrack_mt_save, + .extra_opts = conntrack_mt_opts, + }, + { + .version = XTABLES_VERSION, + .name = "conntrack", + .revision = 2, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)), + .userspacesize = XT_ALIGN(sizeof(struct xt_conntrack_mtinfo2)), + .help = conntrack_mt_help, + .parse = conntrack2_mt6_parse, + .final_check = conntrack_mt_check, + .print = conntrack_mt6_print, + .save = conntrack_mt6_save, + .extra_opts = conntrack_mt_opts, + }, +}; + +void _init(void) +{ + xtables_register_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg)); +} diff --git a/extensions/libxt_conntrack.man b/extensions/libxt_conntrack.man new file mode 100644 index 0000000..ec51ef5 --- /dev/null +++ b/extensions/libxt_conntrack.man @@ -0,0 +1,85 @@ +This module, when combined with connection tracking, allows access to the +connection tracking state for this packet/connection. +.TP +[\fB!\fR] \fB\-\-ctstate\fP \fIstatelist\fP +\fIstatelist\fR is a comma separated list of the connection states to match. +Possible states are listed below. +.TP +[\fB!\fR] \fB\-\-ctproto\fP \fIl4proto\fP +Layer-4 protocol to match (by number or name) +.TP +[\fB!\fR] \fB\-\-ctorigsrc\fP \fIaddress\fP[\fB/\fP\fImask\fP] +.TP +[\fB!\fR] \fB\-\-ctorigdst\fP \fIaddress\fP[\fB/\fP\fImask\fP] +.TP +[\fB!\fR] \fB\-\-ctreplsrc\fP \fIaddress\fP[\fB/\fP\fImask\fP] +.TP +[\fB!\fR] \fB\-\-ctrepldst\fP \fIaddress\fP[\fB/\fP\fImask\fP] +Match against original/reply source/destination address +.TP +[\fB!\fR] \fB\-\-ctorigsrcport\fP \fIport\fP +.TP +[\fB!\fR] \fB\-\-ctorigdstport\fP \fIport\fP +.TP +[\fB!\fR] \fB\-\-ctreplsrcport\fP \fIport\fP +.TP +[\fB!\fR] \fB\-\-ctrepldstport\fP \fIport\fP +Match against original/reply source/destination port (TCP/UDP/etc.) or GRE key. +.TP +[\fB!\fR] \fB\-\-ctstatus\fP \fIstatelist\fP +\fIstatuslist\fR is a comma separated list of the connection statuses to match. +Possible statuses are listed below. +.TP +[\fB!\fR] \fB\-\-ctexpire\fP \fItime\fP[\fB:\fP\fItime\fP] +Match remaining lifetime in seconds against given value or range of values +(inclusive) +.TP +\fB\-\-ctdir\fP {\fBORIGINAL\fP|\fBREPLY\fP} +Match packets that are flowing in the specified direction. If this flag is not +specified at all, matches packets in both directions. +.PP +States for \fB\-\-ctstate\fP: +.TP +\fBINVALID\fR +meaning that the packet is associated with no known connection +.TP +\fBNEW\fR +meaning that the packet has started a new connection, or otherwise associated +with a connection which has not seen packets in both directions, and +.TP +\fBESTABLISHED\fR +meaning that the packet is associated with a connection which has seen packets +in both directions, +.TP +\fBRELATED\fR +meaning that the packet is starting a new connection, but is associated with an +existing connection, such as an FTP data transfer, or an ICMP error. +.TP +\fBUNTRACKED\fR +meaning that the packet is not tracked at all, which happens if you use +the NOTRACK target in raw table. +.TP +\fBSNAT\fR +A virtual state, matching if the original source address differs from the reply +destination. +.TP +\fBDNAT\fR +A virtual state, matching if the original destination differs from the reply +source. +.PP +Statuses for \fB\-\-ctstatus\fP: +.TP +\fBNONE\fR +None of the below. +.TP +\fBEXPECTED\fR +This is an expected connection (i.e. a conntrack helper set it up) +.TP +\fBSEEN_REPLY\fR +Conntrack has seen packets in both directions. +.TP +\fBASSURED\fR +Conntrack entry should never be early-expired. +.TP +\fBCONFIRMED\fR +Connection is confirmed: originating packet has left box. diff --git a/extensions/libxt_dccp.c b/extensions/libxt_dccp.c new file mode 100644 index 0000000..8d0b13a --- /dev/null +++ b/extensions/libxt_dccp.c @@ -0,0 +1,354 @@ +/* Shared library add-on to iptables for DCCP matching + * + * (C) 2005 by Harald Welte <laforge@netfilter.org> + * + * This program is distributed under the terms of GNU GPL v2, 1991 + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <netdb.h> +#include <ctype.h> + +#include <netinet/in.h> +#include <xtables.h> +#include <linux/dccp.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_dccp.h> + +#if 0 +#define DEBUGP(format, first...) printf(format, ##first) +#define static +#else +#define DEBUGP(format, fist...) +#endif + +static void dccp_init(struct xt_entry_match *m) +{ + struct xt_dccp_info *einfo = (struct xt_dccp_info *)m->data; + + memset(einfo, 0, sizeof(struct xt_dccp_info)); +} + +static void dccp_help(void) +{ + printf( +"dccp match options\n" +"[!] --source-port port[:port] match source port(s)\n" +" --sport ...\n" +"[!] --destination-port port[:port] match destination port(s)\n" +" --dport ...\n"); +} + +static const struct option dccp_opts[] = { + { .name = "source-port", .has_arg = 1, .val = '1' }, + { .name = "sport", .has_arg = 1, .val = '1' }, + { .name = "destination-port", .has_arg = 1, .val = '2' }, + { .name = "dport", .has_arg = 1, .val = '2' }, + { .name = "dccp-types", .has_arg = 1, .val = '3' }, + { .name = "dccp-option", .has_arg = 1, .val = '4' }, + { .name = NULL } +}; + +static void +parse_dccp_ports(const char *portstring, + u_int16_t *ports) +{ + char *buffer; + char *cp; + + buffer = strdup(portstring); + DEBUGP("%s\n", portstring); + if ((cp = strchr(buffer, ':')) == NULL) { + ports[0] = ports[1] = xtables_parse_port(buffer, "dccp"); + } + else { + *cp = '\0'; + cp++; + + ports[0] = buffer[0] ? xtables_parse_port(buffer, "dccp") : 0; + ports[1] = cp[0] ? xtables_parse_port(cp, "dccp") : 0xFFFF; + + if (ports[0] > ports[1]) + xtables_error(PARAMETER_PROBLEM, + "invalid portrange (min > max)"); + } + free(buffer); +} + +static const char *const dccp_pkt_types[] = { + [DCCP_PKT_REQUEST] = "REQUEST", + [DCCP_PKT_RESPONSE] = "RESPONSE", + [DCCP_PKT_DATA] = "DATA", + [DCCP_PKT_ACK] = "ACK", + [DCCP_PKT_DATAACK] = "DATAACK", + [DCCP_PKT_CLOSEREQ] = "CLOSEREQ", + [DCCP_PKT_CLOSE] = "CLOSE", + [DCCP_PKT_RESET] = "RESET", + [DCCP_PKT_SYNC] = "SYNC", + [DCCP_PKT_SYNCACK] = "SYNCACK", + [DCCP_PKT_INVALID] = "INVALID", +}; + +static u_int16_t +parse_dccp_types(const char *typestring) +{ + u_int16_t typemask = 0; + char *ptr, *buffer; + + buffer = strdup(typestring); + + for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) { + unsigned int i; + for (i = 0; i < ARRAY_SIZE(dccp_pkt_types); ++i) + if (!strcasecmp(dccp_pkt_types[i], ptr)) { + typemask |= (1 << i); + break; + } + if (i == ARRAY_SIZE(dccp_pkt_types)) + xtables_error(PARAMETER_PROBLEM, + "Unknown DCCP type `%s'", ptr); + } + + free(buffer); + return typemask; +} + +static u_int8_t parse_dccp_option(char *optstring) +{ + unsigned int ret; + + if (!xtables_strtoui(optstring, NULL, &ret, 1, UINT8_MAX)) + xtables_error(PARAMETER_PROBLEM, "Bad DCCP option \"%s\"", + optstring); + + return ret; +} + +static int +dccp_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_dccp_info *einfo + = (struct xt_dccp_info *)(*match)->data; + + switch (c) { + case '1': + if (*flags & XT_DCCP_SRC_PORTS) + xtables_error(PARAMETER_PROBLEM, + "Only one `--source-port' allowed"); + einfo->flags |= XT_DCCP_SRC_PORTS; + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_dccp_ports(optarg, einfo->spts); + if (invert) + einfo->invflags |= XT_DCCP_SRC_PORTS; + *flags |= XT_DCCP_SRC_PORTS; + break; + + case '2': + if (*flags & XT_DCCP_DEST_PORTS) + xtables_error(PARAMETER_PROBLEM, + "Only one `--destination-port' allowed"); + einfo->flags |= XT_DCCP_DEST_PORTS; + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_dccp_ports(optarg, einfo->dpts); + if (invert) + einfo->invflags |= XT_DCCP_DEST_PORTS; + *flags |= XT_DCCP_DEST_PORTS; + break; + + case '3': + if (*flags & XT_DCCP_TYPE) + xtables_error(PARAMETER_PROBLEM, + "Only one `--dccp-types' allowed"); + einfo->flags |= XT_DCCP_TYPE; + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + einfo->typemask = parse_dccp_types(optarg); + if (invert) + einfo->invflags |= XT_DCCP_TYPE; + *flags |= XT_DCCP_TYPE; + break; + + case '4': + if (*flags & XT_DCCP_OPTION) + xtables_error(PARAMETER_PROBLEM, + "Only one `--dccp-option' allowed"); + einfo->flags |= XT_DCCP_OPTION; + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + einfo->option = parse_dccp_option(optarg); + if (invert) + einfo->invflags |= XT_DCCP_OPTION; + *flags |= XT_DCCP_OPTION; + break; + default: + return 0; + } + return 1; +} + +static char * +port_to_service(int port) +{ + struct servent *service; + + if ((service = getservbyport(htons(port), "dccp"))) + return service->s_name; + + return NULL; +} + +static void +print_port(u_int16_t port, int numeric) +{ + char *service; + + if (numeric || (service = port_to_service(port)) == NULL) + printf("%u", port); + else + printf("%s", service); +} + +static void +print_ports(const char *name, u_int16_t min, u_int16_t max, + int invert, int numeric) +{ + const char *inv = invert ? "!" : ""; + + if (min != 0 || max != 0xFFFF || invert) { + printf("%s", name); + if (min == max) { + printf(":%s", inv); + print_port(min, numeric); + } else { + printf("s:%s", inv); + print_port(min, numeric); + printf(":"); + print_port(max, numeric); + } + printf(" "); + } +} + +static void +print_types(u_int16_t types, int inverted, int numeric) +{ + int have_type = 0; + + if (inverted) + printf("! "); + + while (types) { + unsigned int i; + + for (i = 0; !(types & (1 << i)); i++); + + if (have_type) + printf(","); + else + have_type = 1; + + if (numeric) + printf("%u", i); + else + printf("%s", dccp_pkt_types[i]); + + types &= ~(1 << i); + } +} + +static void +print_option(u_int8_t option, int invert, int numeric) +{ + if (option || invert) + printf("option=%s%u ", invert ? "!" : "", option); +} + +static void +dccp_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_dccp_info *einfo = + (const struct xt_dccp_info *)match->data; + + printf("dccp "); + + if (einfo->flags & XT_DCCP_SRC_PORTS) { + print_ports("spt", einfo->spts[0], einfo->spts[1], + einfo->invflags & XT_DCCP_SRC_PORTS, + numeric); + } + + if (einfo->flags & XT_DCCP_DEST_PORTS) { + print_ports("dpt", einfo->dpts[0], einfo->dpts[1], + einfo->invflags & XT_DCCP_DEST_PORTS, + numeric); + } + + if (einfo->flags & XT_DCCP_TYPE) { + print_types(einfo->typemask, + einfo->invflags & XT_DCCP_TYPE, + numeric); + } + + if (einfo->flags & XT_DCCP_OPTION) { + print_option(einfo->option, + einfo->invflags & XT_DCCP_OPTION, numeric); + } +} + +static void dccp_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_dccp_info *einfo = + (const struct xt_dccp_info *)match->data; + + if (einfo->flags & XT_DCCP_SRC_PORTS) { + if (einfo->invflags & XT_DCCP_SRC_PORTS) + printf("! "); + if (einfo->spts[0] != einfo->spts[1]) + printf("--sport %u:%u ", + einfo->spts[0], einfo->spts[1]); + else + printf("--sport %u ", einfo->spts[0]); + } + + if (einfo->flags & XT_DCCP_DEST_PORTS) { + if (einfo->invflags & XT_DCCP_DEST_PORTS) + printf("! "); + if (einfo->dpts[0] != einfo->dpts[1]) + printf("--dport %u:%u ", + einfo->dpts[0], einfo->dpts[1]); + else + printf("--dport %u ", einfo->dpts[0]); + } + + if (einfo->flags & XT_DCCP_TYPE) { + printf("--dccp-type "); + print_types(einfo->typemask, einfo->invflags & XT_DCCP_TYPE,0); + } + + if (einfo->flags & XT_DCCP_OPTION) { + printf("--dccp-option %s%u ", + einfo->typemask & XT_DCCP_OPTION ? "! " : "", + einfo->option); + } +} + +static struct xtables_match dccp_match = { + .name = "dccp", + .family = NFPROTO_UNSPEC, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_dccp_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_dccp_info)), + .help = dccp_help, + .init = dccp_init, + .parse = dccp_parse, + .print = dccp_print, + .save = dccp_save, + .extra_opts = dccp_opts, +}; + +void _init(void) +{ + xtables_register_match(&dccp_match); +} diff --git a/extensions/libxt_dccp.man b/extensions/libxt_dccp.man new file mode 100644 index 0000000..82c3f70 --- /dev/null +++ b/extensions/libxt_dccp.man @@ -0,0 +1,12 @@ +.TP +[\fB!\fP] \fB\-\-source\-port\fP,\fB\-\-sport\fP \fIport\fP[\fB:\fP\fIport\fP] +.TP +[\fB!\fP] \fB\-\-destination\-port\fP,\fB\-\-dport\fP \fIport\fP[\fB:\fP\fIport\fP] +.TP +[\fB!\fP] \fB\-\-dccp\-types\fP \fImask\fP +Match when the DCCP packet type is one of 'mask'. 'mask' is a comma-separated +list of packet types. Packet types are: +.BR "REQUEST RESPONSE DATA ACK DATAACK CLOSEREQ CLOSE RESET SYNC SYNCACK INVALID" . +.TP +[\fB!\fP] \fB\-\-dccp\-option\fP \fInumber\fP +Match if DCP option set. diff --git a/extensions/libxt_dscp.c b/extensions/libxt_dscp.c new file mode 100644 index 0000000..1569f7d --- /dev/null +++ b/extensions/libxt_dscp.c @@ -0,0 +1,150 @@ +/* Shared library add-on to iptables for DSCP + * + * (C) 2002 by Harald Welte <laforge@gnumonks.org> + * + * This program is distributed under the terms of GNU GPL v2, 1991 + * + * libipt_dscp.c borrowed heavily from libipt_tos.c + * + * --class support added by Iain Barnes + * + * For a list of DSCP codepoints see + * http://www.iana.org/assignments/dscp-registry + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_dscp.h> + +/* This is evil, but it's my code - HW*/ +#include "dscp_helper.c" + +static void dscp_help(void) +{ + printf( +"dscp match options\n" +"[!] --dscp value Match DSCP codepoint with numerical value\n" +" This value can be in decimal (ex: 32)\n" +" or in hex (ex: 0x20)\n" +"[!] --dscp-class name Match the DiffServ class. This value may\n" +" be any of the BE,EF, AFxx or CSx classes\n" +"\n" +" These two options are mutually exclusive !\n"); +} + +static const struct option dscp_opts[] = { + { "dscp", 1, NULL, 'F' }, + { "dscp-class", 1, NULL, 'G' }, + { .name = NULL } +}; + +static void +parse_dscp(const char *s, struct xt_dscp_info *dinfo) +{ + unsigned int dscp; + + if (!xtables_strtoui(s, NULL, &dscp, 0, UINT8_MAX)) + xtables_error(PARAMETER_PROBLEM, + "Invalid dscp `%s'\n", s); + + if (dscp > XT_DSCP_MAX) + xtables_error(PARAMETER_PROBLEM, + "DSCP `%d` out of range\n", dscp); + + dinfo->dscp = dscp; +} + + +static void +parse_class(const char *s, struct xt_dscp_info *dinfo) +{ + unsigned int dscp = class_to_dscp(s); + + /* Assign the value */ + dinfo->dscp = dscp; +} + + +static int +dscp_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_dscp_info *dinfo + = (struct xt_dscp_info *)(*match)->data; + + switch (c) { + case 'F': + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "DSCP match: Only use --dscp ONCE!"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_dscp(optarg, dinfo); + if (invert) + dinfo->invert = 1; + *flags = 1; + break; + + case 'G': + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "DSCP match: Only use --dscp-class ONCE!"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_class(optarg, dinfo); + if (invert) + dinfo->invert = 1; + *flags = 1; + break; + + default: + return 0; + } + + return 1; +} + +static void dscp_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "DSCP match: Parameter --dscp is required"); +} + +static void +dscp_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_dscp_info *dinfo = + (const struct xt_dscp_info *)match->data; + printf("DSCP match %s0x%02x", dinfo->invert ? "!" : "", dinfo->dscp); +} + +static void dscp_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_dscp_info *dinfo = + (const struct xt_dscp_info *)match->data; + + printf("%s--dscp 0x%02x ", dinfo->invert ? "! " : "", dinfo->dscp); +} + +static struct xtables_match dscp_match = { + .family = NFPROTO_UNSPEC, + .name = "dscp", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_dscp_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_dscp_info)), + .help = dscp_help, + .parse = dscp_parse, + .final_check = dscp_check, + .print = dscp_print, + .save = dscp_save, + .extra_opts = dscp_opts, +}; + +void _init(void) +{ + xtables_register_match(&dscp_match); +} diff --git a/extensions/libxt_dscp.man b/extensions/libxt_dscp.man new file mode 100644 index 0000000..63a17da --- /dev/null +++ b/extensions/libxt_dscp.man @@ -0,0 +1,10 @@ +This module matches the 6 bit DSCP field within the TOS field in the +IP header. DSCP has superseded TOS within the IETF. +.TP +[\fB!\fP] \fB\-\-dscp\fP \fIvalue\fP +Match against a numeric (decimal or hex) value [0-63]. +.TP +[\fB!\fP] \fB\-\-dscp\-class\fP \fIclass\fP +Match the DiffServ class. This value may be any of the +BE, EF, AFxx or CSx classes. It will then be converted +into its according numeric value. diff --git a/extensions/libxt_esp.c b/extensions/libxt_esp.c new file mode 100644 index 0000000..18218f4 --- /dev/null +++ b/extensions/libxt_esp.c @@ -0,0 +1,169 @@ +/* Shared library add-on to iptables to add ESP support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <errno.h> +#include <limits.h> + +#include <xtables.h> +#include <linux/netfilter/xt_esp.h> + +static void esp_help(void) +{ + printf( +"esp match options:\n" +"[!] --espspi spi[:spi]\n" +" match spi (range)\n"); +} + +static const struct option esp_opts[] = { + { "espspi", 1, NULL, '1' }, + { .name = NULL } +}; + +static u_int32_t +parse_esp_spi(const char *spistr) +{ + unsigned long int spi; + char* ep; + + spi = strtoul(spistr,&ep,0) ; + + if ( spistr == ep ) { + xtables_error(PARAMETER_PROBLEM, + "ESP no valid digits in spi `%s'", spistr); + } + if ( spi == ULONG_MAX && errno == ERANGE ) { + xtables_error(PARAMETER_PROBLEM, + "spi `%s' specified too big: would overflow", spistr); + } + if ( *spistr != '\0' && *ep != '\0' ) { + xtables_error(PARAMETER_PROBLEM, + "ESP error parsing spi `%s'", spistr); + } + return spi; +} + +static void +parse_esp_spis(const char *spistring, u_int32_t *spis) +{ + char *buffer; + char *cp; + + buffer = strdup(spistring); + if ((cp = strchr(buffer, ':')) == NULL) + spis[0] = spis[1] = parse_esp_spi(buffer); + else { + *cp = '\0'; + cp++; + + spis[0] = buffer[0] ? parse_esp_spi(buffer) : 0; + spis[1] = cp[0] ? parse_esp_spi(cp) : 0xFFFFFFFF; + if (spis[0] > spis[1]) + xtables_error(PARAMETER_PROBLEM, + "Invalid ESP spi range: %s", spistring); + } + free(buffer); +} + +static void esp_init(struct xt_entry_match *m) +{ + struct xt_esp *espinfo = (struct xt_esp *)m->data; + + espinfo->spis[1] = 0xFFFFFFFF; +} + +#define ESP_SPI 0x01 + +static int +esp_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_esp *espinfo = (struct xt_esp *)(*match)->data; + + switch (c) { + case '1': + if (*flags & ESP_SPI) + xtables_error(PARAMETER_PROBLEM, + "Only one `--espspi' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_esp_spis(optarg, espinfo->spis); + if (invert) + espinfo->invflags |= XT_ESP_INV_SPI; + *flags |= ESP_SPI; + break; + default: + return 0; + } + + return 1; +} + +static void +print_spis(const char *name, u_int32_t min, u_int32_t max, + int invert) +{ + const char *inv = invert ? "!" : ""; + + if (min != 0 || max != 0xFFFFFFFF || invert) { + if (min == max) + printf("%s:%s%u ", name, inv, min); + else + printf("%ss:%s%u:%u ", name, inv, min, max); + } +} + +static void +esp_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_esp *esp = (struct xt_esp *)match->data; + + printf("esp "); + print_spis("spi", esp->spis[0], esp->spis[1], + esp->invflags & XT_ESP_INV_SPI); + if (esp->invflags & ~XT_ESP_INV_MASK) + printf("Unknown invflags: 0x%X ", + esp->invflags & ~XT_ESP_INV_MASK); +} + +static void esp_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_esp *espinfo = (struct xt_esp *)match->data; + + if (!(espinfo->spis[0] == 0 + && espinfo->spis[1] == 0xFFFFFFFF)) { + printf("%s--espspi ", + (espinfo->invflags & XT_ESP_INV_SPI) ? "! " : ""); + if (espinfo->spis[0] + != espinfo->spis[1]) + printf("%u:%u ", + espinfo->spis[0], + espinfo->spis[1]); + else + printf("%u ", + espinfo->spis[0]); + } + +} + +static struct xtables_match esp_match = { + .family = NFPROTO_UNSPEC, + .name = "esp", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_esp)), + .userspacesize = XT_ALIGN(sizeof(struct xt_esp)), + .help = esp_help, + .init = esp_init, + .parse = esp_parse, + .print = esp_print, + .save = esp_save, + .extra_opts = esp_opts, +}; + +void +_init(void) +{ + xtables_register_match(&esp_match); +} diff --git a/extensions/libxt_esp.man b/extensions/libxt_esp.man new file mode 100644 index 0000000..699a41c --- /dev/null +++ b/extensions/libxt_esp.man @@ -0,0 +1,3 @@ +This module matches the SPIs in ESP header of IPsec packets. +.TP +[\fB!\fP] \fB\-\-espspi\fP \fIspi\fP[\fB:\fP\fIspi\fP] diff --git a/extensions/libxt_hashlimit.c b/extensions/libxt_hashlimit.c new file mode 100644 index 0000000..7442dfc --- /dev/null +++ b/extensions/libxt_hashlimit.c @@ -0,0 +1,711 @@ +/* ip6tables match extension for limiting packets per destination + * + * (C) 2003-2004 by Harald Welte <laforge@netfilter.org> + * + * Development of this code was funded by Astaro AG, http://www.astaro.com/ + * + * Based on ipt_limit.c by + * Jérôme de Vivie <devivie@info.enserb.u-bordeaux.fr> + * Hervé Eychenne <rv@wallfire.org> + * + * Error corections by nmalykh@bilim.com (22.01.2005) + */ +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <stddef.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_hashlimit.h> + +#define XT_HASHLIMIT_BURST 5 + +/* miliseconds */ +#define XT_HASHLIMIT_GCINTERVAL 1000 +#define XT_HASHLIMIT_EXPIRE 10000 + +static void hashlimit_help(void) +{ + printf( +"hashlimit match options:\n" +"--hashlimit <avg> max average match rate\n" +" [Packets per second unless followed by \n" +" /sec /minute /hour /day postfixes]\n" +"--hashlimit-mode <mode> mode is a comma-separated list of\n" +" dstip,srcip,dstport,srcport\n" +"--hashlimit-name <name> name for /proc/net/ipt_hashlimit/\n" +"[--hashlimit-burst <num>] number to match in a burst, default %u\n" +"[--hashlimit-htable-size <num>] number of hashtable buckets\n" +"[--hashlimit-htable-max <num>] number of hashtable entries\n" +"[--hashlimit-htable-gcinterval] interval between garbage collection runs\n" +"[--hashlimit-htable-expire] after which time are idle entries expired?\n", +XT_HASHLIMIT_BURST); +} + +static void hashlimit_mt_help(void) +{ + printf( +"hashlimit match options:\n" +" --hashlimit-upto <avg> max average match rate\n" +" [Packets per second unless followed by \n" +" /sec /minute /hour /day postfixes]\n" +" --hashlimit-above <avg> min average match rate\n" +" --hashlimit-mode <mode> mode is a comma-separated list of\n" +" dstip,srcip,dstport,srcport (or none)\n" +" --hashlimit-srcmask <length> source address grouping prefix length\n" +" --hashlimit-dstmask <length> destination address grouping prefix length\n" +" --hashlimit-name <name> name for /proc/net/ipt_hashlimit\n" +" --hashlimit-burst <num> number to match in a burst, default %u\n" +" --hashlimit-htable-size <num> number of hashtable buckets\n" +" --hashlimit-htable-max <num> number of hashtable entries\n" +" --hashlimit-htable-gcinterval interval between garbage collection runs\n" +" --hashlimit-htable-expire after which time are idle entries expired?\n" +"\n", XT_HASHLIMIT_BURST); +} + +static const struct option hashlimit_opts[] = { + { "hashlimit", 1, NULL, '%' }, + { "hashlimit-burst", 1, NULL, '$' }, + { "hashlimit-htable-size", 1, NULL, '&' }, + { "hashlimit-htable-max", 1, NULL, '*' }, + { "hashlimit-htable-gcinterval", 1, NULL, '(' }, + { "hashlimit-htable-expire", 1, NULL, ')' }, + { "hashlimit-mode", 1, NULL, '_' }, + { "hashlimit-name", 1, NULL, '"' }, + { .name = NULL } +}; + +static const struct option hashlimit_mt_opts[] = { + {.name = "hashlimit-upto", .has_arg = true, .val = '%'}, + {.name = "hashlimit-above", .has_arg = true, .val = '^'}, + {.name = "hashlimit", .has_arg = true, .val = '%'}, + {.name = "hashlimit-srcmask", .has_arg = true, .val = '<'}, + {.name = "hashlimit-dstmask", .has_arg = true, .val = '>'}, + {.name = "hashlimit-burst", .has_arg = true, .val = '$'}, + {.name = "hashlimit-htable-size", .has_arg = true, .val = '&'}, + {.name = "hashlimit-htable-max", .has_arg = true, .val = '*'}, + {.name = "hashlimit-htable-gcinterval", .has_arg = true, .val = '('}, + {.name = "hashlimit-htable-expire", .has_arg = true, .val = ')'}, + {.name = "hashlimit-mode", .has_arg = true, .val = '_'}, + {.name = "hashlimit-name", .has_arg = true, .val = '"'}, + {}, +}; + +static +int parse_rate(const char *rate, u_int32_t *val) +{ + const char *delim; + u_int32_t r; + u_int32_t mult = 1; /* Seconds by default. */ + + delim = strchr(rate, '/'); + if (delim) { + if (strlen(delim+1) == 0) + return 0; + + if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0) + mult = 1; + else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0) + mult = 60; + else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0) + mult = 60*60; + else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0) + mult = 24*60*60; + else + return 0; + } + r = atoi(rate); + if (!r) + return 0; + + /* This would get mapped to infinite (1/day is minimum they + can specify, so we're ok at that end). */ + if (r / mult > XT_HASHLIMIT_SCALE) + xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate); + + *val = XT_HASHLIMIT_SCALE * mult / r; + return 1; +} + +static void hashlimit_init(struct xt_entry_match *m) +{ + struct xt_hashlimit_info *r = (struct xt_hashlimit_info *)m->data; + + r->cfg.mode = 0; + r->cfg.burst = XT_HASHLIMIT_BURST; + r->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL; + r->cfg.expire = XT_HASHLIMIT_EXPIRE; + +} + +static void hashlimit_mt4_init(struct xt_entry_match *match) +{ + struct xt_hashlimit_mtinfo1 *info = (void *)match->data; + + info->cfg.mode = 0; + info->cfg.burst = XT_HASHLIMIT_BURST; + info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL; + info->cfg.expire = XT_HASHLIMIT_EXPIRE; + info->cfg.srcmask = 32; + info->cfg.dstmask = 32; +} + +static void hashlimit_mt6_init(struct xt_entry_match *match) +{ + struct xt_hashlimit_mtinfo1 *info = (void *)match->data; + + info->cfg.mode = 0; + info->cfg.burst = XT_HASHLIMIT_BURST; + info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL; + info->cfg.expire = XT_HASHLIMIT_EXPIRE; + info->cfg.srcmask = 128; + info->cfg.dstmask = 128; +} + +/* Parse a 'mode' parameter into the required bitmask */ +static int parse_mode(uint32_t *mode, char *option_arg) +{ + char *tok; + char *arg = strdup(option_arg); + + if (!arg) + return -1; + + for (tok = strtok(arg, ",|"); + tok; + tok = strtok(NULL, ",|")) { + if (!strcmp(tok, "dstip")) + *mode |= XT_HASHLIMIT_HASH_DIP; + else if (!strcmp(tok, "srcip")) + *mode |= XT_HASHLIMIT_HASH_SIP; + else if (!strcmp(tok, "srcport")) + *mode |= XT_HASHLIMIT_HASH_SPT; + else if (!strcmp(tok, "dstport")) + *mode |= XT_HASHLIMIT_HASH_DPT; + else { + free(arg); + return -1; + } + } + free(arg); + return 0; +} + +enum { + PARAM_LIMIT = 1 << 0, + PARAM_BURST = 1 << 1, + PARAM_MODE = 1 << 2, + PARAM_NAME = 1 << 3, + PARAM_SIZE = 1 << 4, + PARAM_MAX = 1 << 5, + PARAM_GCINTERVAL = 1 << 6, + PARAM_EXPIRE = 1 << 7, + PARAM_SRCMASK = 1 << 8, + PARAM_DSTMASK = 1 << 9, +}; + +static int +hashlimit_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_hashlimit_info *r = + (struct xt_hashlimit_info *)(*match)->data; + unsigned int num; + + switch(c) { + case '%': + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", "--hashlimit", + *flags & PARAM_LIMIT); + if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) break; + if (!parse_rate(optarg, &r->cfg.avg)) + xtables_error(PARAMETER_PROBLEM, + "bad rate `%s'", optarg); + *flags |= PARAM_LIMIT; + break; + + case '$': + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", "--hashlimit-burst", + *flags & PARAM_BURST); + if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) break; + if (!xtables_strtoui(optarg, NULL, &num, 0, 10000)) + xtables_error(PARAMETER_PROBLEM, + "bad --hashlimit-burst `%s'", optarg); + r->cfg.burst = num; + *flags |= PARAM_BURST; + break; + case '&': + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", "--hashlimit-htable-size", + *flags & PARAM_SIZE); + if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) break; + if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX)) + xtables_error(PARAMETER_PROBLEM, + "bad --hashlimit-htable-size: `%s'", optarg); + r->cfg.size = num; + *flags |= PARAM_SIZE; + break; + case '*': + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", "--hashlimit-htable-max", + *flags & PARAM_MAX); + if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) break; + if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX)) + xtables_error(PARAMETER_PROBLEM, + "bad --hashlimit-htable-max: `%s'", optarg); + r->cfg.max = num; + *flags |= PARAM_MAX; + break; + case '(': + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", + "--hashlimit-htable-gcinterval", + *flags & PARAM_GCINTERVAL); + if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) break; + if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX)) + xtables_error(PARAMETER_PROBLEM, + "bad --hashlimit-htable-gcinterval: `%s'", + optarg); + /* FIXME: not HZ dependent!! */ + r->cfg.gc_interval = num; + *flags |= PARAM_GCINTERVAL; + break; + case ')': + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", + "--hashlimit-htable-expire", *flags & PARAM_EXPIRE); + if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) break; + if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX)) + xtables_error(PARAMETER_PROBLEM, + "bad --hashlimit-htable-expire: `%s'", optarg); + /* FIXME: not HZ dependent */ + r->cfg.expire = num; + *flags |= PARAM_EXPIRE; + break; + case '_': + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", "--hashlimit-mode", + *flags & PARAM_MODE); + if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) break; + if (parse_mode(&r->cfg.mode, optarg) < 0) + xtables_error(PARAMETER_PROBLEM, + "bad --hashlimit-mode: `%s'\n", optarg); + *flags |= PARAM_MODE; + break; + case '"': + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", "--hashlimit-name", + *flags & PARAM_NAME); + if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) break; + if (strlen(optarg) == 0) + xtables_error(PARAMETER_PROBLEM, "Zero-length name?"); + strncpy(r->name, optarg, sizeof(r->name)); + *flags |= PARAM_NAME; + break; + default: + return 0; + } + + if (invert) + xtables_error(PARAMETER_PROBLEM, + "hashlimit does not support invert"); + + return 1; +} + +static int +hashlimit_mt_parse(struct xt_hashlimit_mtinfo1 *info, unsigned int *flags, + int c, int invert, unsigned int maxmask) +{ + unsigned int num; + + switch(c) { + case '%': /* --hashlimit / --hashlimit-below */ + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", "--hashlimit-upto", + *flags & PARAM_LIMIT); + if (invert) + info->cfg.mode |= XT_HASHLIMIT_INVERT; + if (!parse_rate(optarg, &info->cfg.avg)) + xtables_param_act(XTF_BAD_VALUE, "hashlimit", + "--hashlimit-upto", optarg); + *flags |= PARAM_LIMIT; + return true; + + case '^': /* --hashlimit-above == !--hashlimit-below */ + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", "--hashlimit-above", + *flags & PARAM_LIMIT); + if (!invert) + info->cfg.mode |= XT_HASHLIMIT_INVERT; + if (!parse_rate(optarg, &info->cfg.avg)) + xtables_param_act(XTF_BAD_VALUE, "hashlimit", + "--hashlimit-above", optarg); + *flags |= PARAM_LIMIT; + return true; + + case '$': /* --hashlimit-burst */ + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", "--hashlimit-burst", + *flags & PARAM_BURST); + if (!xtables_strtoui(optarg, NULL, &num, 0, 10000)) + xtables_param_act(XTF_BAD_VALUE, "hashlimit", + "--hashlimit-burst", optarg); + info->cfg.burst = num; + *flags |= PARAM_BURST; + return true; + + case '&': /* --hashlimit-htable-size */ + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", "--hashlimit-htable-size", + *flags & PARAM_SIZE); + if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "hashlimit", + "--hashlimit-htable-size", optarg); + info->cfg.size = num; + *flags |= PARAM_SIZE; + return true; + + case '*': /* --hashlimit-htable-max */ + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", "--hashlimit-htable-max", + *flags & PARAM_MAX); + if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "hashlimit", + "--hashlimit-htable-max", optarg); + info->cfg.max = num; + *flags |= PARAM_MAX; + return true; + + case '(': /* --hashlimit-htable-gcinterval */ + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", + "--hashlimit-htable-gcinterval", + *flags & PARAM_GCINTERVAL); + if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "hashlimit", + "--hashlimit-htable-gcinterval", optarg); + /* FIXME: not HZ dependent!! */ + info->cfg.gc_interval = num; + *flags |= PARAM_GCINTERVAL; + return true; + + case ')': /* --hashlimit-htable-expire */ + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", + "--hashlimit-htable-expire", *flags & PARAM_EXPIRE); + if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "hashlimit", + "--hashlimit-htable-expire", optarg); + /* FIXME: not HZ dependent */ + info->cfg.expire = num; + *flags |= PARAM_EXPIRE; + return true; + + case '_': + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", "--hashlimit-mode", + *flags & PARAM_MODE); + if (parse_mode(&info->cfg.mode, optarg) < 0) + xtables_param_act(XTF_BAD_VALUE, "hashlimit", + "--hashlimit-mode", optarg); + *flags |= PARAM_MODE; + return true; + + case '"': /* --hashlimit-name */ + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", "--hashlimit-name", + *flags & PARAM_NAME); + if (strlen(optarg) == 0) + xtables_error(PARAMETER_PROBLEM, "Zero-length name?"); + strncpy(info->name, optarg, sizeof(info->name)); + info->name[sizeof(info->name)-1] = '\0'; + *flags |= PARAM_NAME; + return true; + + case '<': /* --hashlimit-srcmask */ + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", "--hashlimit-srcmask", + *flags & PARAM_SRCMASK); + if (!xtables_strtoui(optarg, NULL, &num, 0, maxmask)) + xtables_param_act(XTF_BAD_VALUE, "hashlimit", + "--hashlimit-srcmask", optarg); + info->cfg.srcmask = num; + *flags |= PARAM_SRCMASK; + return true; + + case '>': /* --hashlimit-dstmask */ + xtables_param_act(XTF_ONLY_ONCE, "hashlimit", "--hashlimit-dstmask", + *flags & PARAM_DSTMASK); + if (!xtables_strtoui(optarg, NULL, &num, 0, maxmask)) + xtables_param_act(XTF_BAD_VALUE, "hashlimit", + "--hashlimit-dstmask", optarg); + info->cfg.dstmask = num; + *flags |= PARAM_DSTMASK; + return true; + } + return false; +} + +static int +hashlimit_mt4_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + return hashlimit_mt_parse((void *)(*match)->data, + flags, c, invert, 32); +} + +static int +hashlimit_mt6_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + return hashlimit_mt_parse((void *)(*match)->data, + flags, c, invert, 128); +} + +static void hashlimit_check(unsigned int flags) +{ + if (!(flags & PARAM_LIMIT)) + xtables_error(PARAMETER_PROBLEM, + "You have to specify --hashlimit"); + if (!(flags & PARAM_MODE)) + xtables_error(PARAMETER_PROBLEM, + "You have to specify --hashlimit-mode"); + if (!(flags & PARAM_NAME)) + xtables_error(PARAMETER_PROBLEM, + "You have to specify --hashlimit-name"); +} + +static void hashlimit_mt_check(unsigned int flags) +{ + if (!(flags & PARAM_LIMIT)) + xtables_error(PARAMETER_PROBLEM, "You have to specify " + "--hashlimit-upto or --hashlimit-above"); + if (!(flags & PARAM_NAME)) + xtables_error(PARAMETER_PROBLEM, + "You have to specify --hashlimit-name"); +} + +static const struct rates +{ + const char *name; + u_int32_t mult; +} rates[] = { { "day", XT_HASHLIMIT_SCALE*24*60*60 }, + { "hour", XT_HASHLIMIT_SCALE*60*60 }, + { "min", XT_HASHLIMIT_SCALE*60 }, + { "sec", XT_HASHLIMIT_SCALE } }; + +static void print_rate(u_int32_t period) +{ + unsigned int i; + + for (i = 1; i < ARRAY_SIZE(rates); ++i) + if (period > rates[i].mult + || rates[i].mult/period < rates[i].mult%period) + break; + + printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name); +} + +static void print_mode(unsigned int mode, char separator) +{ + bool prevmode = false; + + if (mode & XT_HASHLIMIT_HASH_SIP) { + fputs("srcip", stdout); + prevmode = 1; + } + if (mode & XT_HASHLIMIT_HASH_SPT) { + if (prevmode) + putchar(separator); + fputs("srcport", stdout); + prevmode = 1; + } + if (mode & XT_HASHLIMIT_HASH_DIP) { + if (prevmode) + putchar(separator); + fputs("dstip", stdout); + prevmode = 1; + } + if (mode & XT_HASHLIMIT_HASH_DPT) { + if (prevmode) + putchar(separator); + fputs("dstport", stdout); + } + putchar(' '); +} + +static void hashlimit_print(const void *ip, + const struct xt_entry_match *match, int numeric) +{ + const struct xt_hashlimit_info *r = (const void *)match->data; + fputs("limit: avg ", stdout); print_rate(r->cfg.avg); + printf("burst %u ", r->cfg.burst); + fputs("mode ", stdout); + print_mode(r->cfg.mode, '-'); + if (r->cfg.size) + printf("htable-size %u ", r->cfg.size); + if (r->cfg.max) + printf("htable-max %u ", r->cfg.max); + if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL) + printf("htable-gcinterval %u ", r->cfg.gc_interval); + if (r->cfg.expire != XT_HASHLIMIT_EXPIRE) + printf("htable-expire %u ", r->cfg.expire); +} + +static void +hashlimit_mt_print(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask) +{ + if (info->cfg.mode & XT_HASHLIMIT_INVERT) + fputs("limit: above ", stdout); + else + fputs("limit: up to ", stdout); + print_rate(info->cfg.avg); + printf("burst %u ", info->cfg.burst); + if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT | + XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) { + fputs("mode ", stdout); + print_mode(info->cfg.mode, '-'); + } + if (info->cfg.size != 0) + printf("htable-size %u ", info->cfg.size); + if (info->cfg.max != 0) + printf("htable-max %u ", info->cfg.max); + if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL) + printf("htable-gcinterval %u ", info->cfg.gc_interval); + if (info->cfg.expire != XT_HASHLIMIT_EXPIRE) + printf("htable-expire %u ", info->cfg.expire); + + if (info->cfg.srcmask != dmask) + printf("srcmask %u ", info->cfg.srcmask); + if (info->cfg.dstmask != dmask) + printf("dstmask %u ", info->cfg.dstmask); +} + +static void +hashlimit_mt4_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data; + + hashlimit_mt_print(info, 32); +} + +static void +hashlimit_mt6_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data; + + hashlimit_mt_print(info, 128); +} + +static void hashlimit_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_hashlimit_info *r = (const void *)match->data; + + fputs("--hashlimit ", stdout); print_rate(r->cfg.avg); + printf("--hashlimit-burst %u ", r->cfg.burst); + + fputs("--hashlimit-mode ", stdout); + print_mode(r->cfg.mode, ','); + + printf("--hashlimit-name %s ", r->name); + + if (r->cfg.size) + printf("--hashlimit-htable-size %u ", r->cfg.size); + if (r->cfg.max) + printf("--hashlimit-htable-max %u ", r->cfg.max); + if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL) + printf("--hashlimit-htable-gcinterval %u ", r->cfg.gc_interval); + if (r->cfg.expire != XT_HASHLIMIT_EXPIRE) + printf("--hashlimit-htable-expire %u ", r->cfg.expire); +} + +static void +hashlimit_mt_save(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask) +{ + if (info->cfg.mode & XT_HASHLIMIT_INVERT) + fputs("--hashlimit-above ", stdout); + else + fputs("--hashlimit-upto ", stdout); + print_rate(info->cfg.avg); + printf("--hashlimit-burst %u ", info->cfg.burst); + + if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT | + XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) { + fputs("--hashlimit-mode ", stdout); + print_mode(info->cfg.mode, ','); + } + + printf("--hashlimit-name %s ", info->name); + + if (info->cfg.size != 0) + printf("--hashlimit-htable-size %u ", info->cfg.size); + if (info->cfg.max != 0) + printf("--hashlimit-htable-max %u ", info->cfg.max); + if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL) + printf("--hashlimit-htable-gcinterval %u ", info->cfg.gc_interval); + if (info->cfg.expire != XT_HASHLIMIT_EXPIRE) + printf("--hashlimit-htable-expire %u ", info->cfg.expire); + + if (info->cfg.srcmask != dmask) + printf("--hashlimit-srcmask %u ", info->cfg.srcmask); + if (info->cfg.dstmask != dmask) + printf("--hashlimit-dstmask %u ", info->cfg.dstmask); +} + +static void +hashlimit_mt4_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data; + + hashlimit_mt_save(info, 32); +} + +static void +hashlimit_mt6_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data; + + hashlimit_mt_save(info, 128); +} + +static struct xtables_match hashlimit_mt_reg[] = { + { + .family = NFPROTO_UNSPEC, + .name = "hashlimit", + .version = XTABLES_VERSION, + .revision = 0, + .size = XT_ALIGN(sizeof(struct xt_hashlimit_info)), + .userspacesize = offsetof(struct xt_hashlimit_info, hinfo), + .help = hashlimit_help, + .init = hashlimit_init, + .parse = hashlimit_parse, + .final_check = hashlimit_check, + .print = hashlimit_print, + .save = hashlimit_save, + .extra_opts = hashlimit_opts, + }, + { + .version = XTABLES_VERSION, + .name = "hashlimit", + .revision = 1, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)), + .userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo), + .help = hashlimit_mt_help, + .init = hashlimit_mt4_init, + .parse = hashlimit_mt4_parse, + .final_check = hashlimit_mt_check, + .print = hashlimit_mt4_print, + .save = hashlimit_mt4_save, + .extra_opts = hashlimit_mt_opts, + }, + { + .version = XTABLES_VERSION, + .name = "hashlimit", + .revision = 1, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)), + .userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo), + .help = hashlimit_mt_help, + .init = hashlimit_mt6_init, + .parse = hashlimit_mt6_parse, + .final_check = hashlimit_mt_check, + .print = hashlimit_mt6_print, + .save = hashlimit_mt6_save, + .extra_opts = hashlimit_mt_opts, + }, +}; + +void _init(void) +{ + xtables_register_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg)); +} diff --git a/extensions/libxt_hashlimit.man b/extensions/libxt_hashlimit.man new file mode 100644 index 0000000..b870f55 --- /dev/null +++ b/extensions/libxt_hashlimit.man @@ -0,0 +1,59 @@ +\fBhashlimit\fR uses hash buckets to express a rate limiting match (like the +\fBlimit\fR match) for a group of connections using a \fBsingle\fR iptables +rule. Grouping can be done per-hostgroup (source and/or destination address) +and/or per-port. It gives you the ability to express "\fIN\fR packets per time +quantum per group": +.TP +matching on source host +"1000 packets per second for every host in 192.168.0.0/16" +.TP +matching on source port +"100 packets per second for every service of 192.168.1.1" +.TP +matching on subnet +"10000 packets per minute for every /28 subnet in 10.0.0.0/8" +.PP +A hash limit option (\fB\-\-hashlimit\-upto\fP, \fB\-\-hashlimit\-above\fP) and +\fB\-\-hashlimit\-name\fP are required. +.TP +\fB\-\-hashlimit\-upto\fP \fIamount\fP[\fB/second\fP|\fB/minute\fP|\fB/hour\fP|\fB/day\fP] +Match if the rate is below or equal to \fIamount\fR/quantum. It is specified as +a number, with an optional time quantum suffix; the default is 3/hour. +.TP +\fB\-\-hashlimit\-above\fP \fIamount\fP[\fB/second\fP|\fB/minute\fP|\fB/hour\fP|\fB/day\fP] +Match if the rate is above \fIamount\fR/quantum. +.TP +\fB\-\-hashlimit\-burst\fP \fIamount\fP +Maximum initial number of packets to match: this number gets recharged by one +every time the limit specified above is not reached, up to this number; the +default is 5. +.TP +\fB\-\-hashlimit\-mode\fP {\fBsrcip\fP|\fBsrcport\fP|\fBdstip\fP|\fBdstport\fP}\fB,\fP... +A comma-separated list of objects to take into consideration. If no +\-\-hashlimit\-mode option is given, hashlimit acts like limit, but at the +expensive of doing the hash housekeeping. +.TP +\fB\-\-hashlimit\-srcmask\fP \fIprefix\fP +When \-\-hashlimit\-mode srcip is used, all source addresses encountered will be +grouped according to the given prefix length and the so-created subnet will be +subject to hashlimit. \fIprefix\fR must be between (inclusive) 0 and 32. Note +that \-\-hashlimit\-srcmask 0 is basically doing the same thing as not specifying +srcip for \-\-hashlimit\-mode, but is technically more expensive. +.TP +\fB\-\-hashlimit\-dstmask\fP \fIprefix\fP +Like \-\-hashlimit\-srcmask, but for destination addresses. +.TP +\fB\-\-hashlimit\-name\fP \fIfoo\fP +The name for the /proc/net/ipt_hashlimit/foo entry. +.TP +\fB\-\-hashlimit\-htable\-size\fP \fIbuckets\fP +The number of buckets of the hash table +.TP +\fB\-\-hashlimit\-htable\-max\fP \fIentries\fP +Maximum entries in the hash. +.TP +\fB\-\-hashlimit\-htable\-expire\fP \fImsec\fP +After how many milliseconds do hash entries expire. +.TP +\fB\-\-hashlimit\-htable\-gcinterval\fP \fImsec\fP +How many milliseconds between garbage collection intervals. diff --git a/extensions/libxt_helper.c b/extensions/libxt_helper.c new file mode 100644 index 0000000..35b5f15 --- /dev/null +++ b/extensions/libxt_helper.c @@ -0,0 +1,87 @@ +/* Shared library add-on to iptables to add related packet matching support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/xt_helper.h> + +static void helper_help(void) +{ + printf( +"helper match options:\n" +"[!] --helper string Match helper identified by string\n"); +} + +static const struct option helper_opts[] = { + { "helper", 1, NULL, '1' }, + { .name = NULL } +}; + +static int +helper_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_helper_info *info = (struct xt_helper_info *)(*match)->data; + + switch (c) { + case '1': + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "helper match: Only use --helper ONCE!"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + strncpy(info->name, optarg, 29); + info->name[29] = '\0'; + if (invert) + info->invert = 1; + *flags = 1; + break; + + default: + return 0; + } + return 1; +} + +static void helper_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "helper match: You must specify `--helper'"); +} + +static void +helper_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_helper_info *info = (const void *)match->data; + + printf("helper match %s\"%s\" ", info->invert ? "! " : "", info->name); +} + +static void helper_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_helper_info *info = (const void *)match->data; + + printf("%s--helper ",info->invert ? "! " : ""); + xtables_save_string(info->name); +} + +static struct xtables_match helper_match = { + .family = NFPROTO_UNSPEC, + .name = "helper", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_helper_info)), + .help = helper_help, + .parse = helper_parse, + .final_check = helper_check, + .print = helper_print, + .save = helper_save, + .extra_opts = helper_opts, +}; + +void _init(void) +{ + xtables_register_match(&helper_match); +} diff --git a/extensions/libxt_helper.man b/extensions/libxt_helper.man new file mode 100644 index 0000000..772b135 --- /dev/null +++ b/extensions/libxt_helper.man @@ -0,0 +1,11 @@ +This module matches packets related to a specific conntrack-helper. +.TP +[\fB!\fP] \fB\-\-helper\fP \fIstring\fP +Matches packets related to the specified conntrack-helper. +.RS +.PP +string can be "ftp" for packets related to a ftp-session on default port. +For other ports append \-portnr to the value, ie. "ftp\-2121". +.PP +Same rules apply for other conntrack-helpers. +.RE diff --git a/extensions/libxt_iprange.c b/extensions/libxt_iprange.c new file mode 100644 index 0000000..55a2f84 --- /dev/null +++ b/extensions/libxt_iprange.c @@ -0,0 +1,387 @@ +/* Shared library add-on to iptables to add IP range matching support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <netinet/in.h> +#include <xtables.h> +#include <linux/netfilter.h> +#include <linux/netfilter/xt_iprange.h> + +struct ipt_iprange { + /* Inclusive: network order. */ + __be32 min_ip, max_ip; +}; + +struct ipt_iprange_info { + struct ipt_iprange src; + struct ipt_iprange dst; + + /* Flags from above */ + u_int8_t flags; +}; + +enum { + F_SRCIP = 1 << 0, + F_DSTIP = 1 << 1, +}; + +static void iprange_mt_help(void) +{ + printf( +"iprange match options:\n" +"[!] --src-range ip[-ip] Match source IP in the specified range\n" +"[!] --dst-range ip[-ip] Match destination IP in the specified range\n"); +} + +static const struct option iprange_mt_opts[] = { + {.name = "src-range", .has_arg = true, .val = '1'}, + {.name = "dst-range", .has_arg = true, .val = '2'}, + { .name = NULL } +}; + +static void +iprange_parse_spec(const char *from, const char *to, union nf_inet_addr *range, + uint8_t family, const char *optname) +{ + const char *spec[2] = {from, to}; + struct in6_addr *ia6; + struct in_addr *ia4; + unsigned int i; + + memset(range, 0, sizeof(union nf_inet_addr) * 2); + + if (family == NFPROTO_IPV6) { + for (i = 0; i < ARRAY_SIZE(spec); ++i) { + ia6 = xtables_numeric_to_ip6addr(spec[i]); + if (ia6 == NULL) + xtables_param_act(XTF_BAD_VALUE, "iprange", + optname, spec[i]); + range[i].in6 = *ia6; + } + } else { + for (i = 0; i < ARRAY_SIZE(spec); ++i) { + ia4 = xtables_numeric_to_ipaddr(spec[i]); + if (ia4 == NULL) + xtables_param_act(XTF_BAD_VALUE, "iprange", + optname, spec[i]); + range[i].in = *ia4; + } + } +} + +static void iprange_parse_range(char *arg, union nf_inet_addr *range, + u_int8_t family, const char *optname) +{ + char *dash; + + dash = strchr(arg, '-'); + if (dash == NULL) { + iprange_parse_spec(arg, arg, range, family, optname); + return; + } + + *dash = '\0'; + iprange_parse_spec(arg, dash + 1, range, family, optname); + if (memcmp(&range[0], &range[1], sizeof(*range)) > 0) + fprintf(stderr, "xt_iprange: range %s-%s is reversed and " + "will never match\n", arg, dash + 1); +} + +static int iprange_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ipt_iprange_info *info = (struct ipt_iprange_info *)(*match)->data; + union nf_inet_addr range[2]; + + switch (c) { + case '1': + if (*flags & IPRANGE_SRC) + xtables_error(PARAMETER_PROBLEM, + "iprange match: Only use --src-range ONCE!"); + *flags |= IPRANGE_SRC; + + info->flags |= IPRANGE_SRC; + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) + info->flags |= IPRANGE_SRC_INV; + iprange_parse_range(optarg, range, NFPROTO_IPV4, "--src-range"); + info->src.min_ip = range[0].ip; + info->src.max_ip = range[1].ip; + break; + + case '2': + if (*flags & IPRANGE_DST) + xtables_error(PARAMETER_PROBLEM, + "iprange match: Only use --dst-range ONCE!"); + *flags |= IPRANGE_DST; + + info->flags |= IPRANGE_DST; + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) + info->flags |= IPRANGE_DST_INV; + + iprange_parse_range(optarg, range, NFPROTO_IPV4, "--dst-range"); + info->dst.min_ip = range[0].ip; + info->dst.max_ip = range[1].ip; + break; + + default: + return 0; + } + return 1; +} + +static int +iprange_mt4_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_iprange_mtinfo *info = (void *)(*match)->data; + + switch (c) { + case '1': /* --src-range */ + iprange_parse_range(optarg, &info->src_min, NFPROTO_IPV4, + "--src-range"); + info->flags |= IPRANGE_SRC; + if (invert) + info->flags |= IPRANGE_SRC_INV; + *flags |= F_SRCIP; + return true; + + case '2': /* --dst-range */ + iprange_parse_range(optarg, &info->dst_min, NFPROTO_IPV4, + "--dst-range"); + info->flags |= IPRANGE_DST; + if (invert) + info->flags |= IPRANGE_DST_INV; + *flags |= F_DSTIP; + return true; + } + return false; +} + +static int +iprange_mt6_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_iprange_mtinfo *info = (void *)(*match)->data; + + switch (c) { + case '1': /* --src-range */ + iprange_parse_range(optarg, &info->src_min, NFPROTO_IPV6, + "--src-range"); + info->flags |= IPRANGE_SRC; + if (invert) + info->flags |= IPRANGE_SRC_INV; + *flags |= F_SRCIP; + return true; + + case '2': /* --dst-range */ + iprange_parse_range(optarg, &info->dst_min, NFPROTO_IPV6, + "--dst-range"); + info->flags |= IPRANGE_DST; + if (invert) + info->flags |= IPRANGE_DST_INV; + *flags |= F_DSTIP; + return true; + } + return false; +} + +static void iprange_mt_check(unsigned int flags) +{ + if (flags == 0) + xtables_error(PARAMETER_PROBLEM, + "iprange match: You must specify `--src-range' or `--dst-range'"); +} + +static void +print_iprange(const struct ipt_iprange *range) +{ + const unsigned char *byte_min, *byte_max; + + byte_min = (const unsigned char *)&range->min_ip; + byte_max = (const unsigned char *)&range->max_ip; + printf("%u.%u.%u.%u-%u.%u.%u.%u ", + byte_min[0], byte_min[1], byte_min[2], byte_min[3], + byte_max[0], byte_max[1], byte_max[2], byte_max[3]); +} + +static void iprange_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ipt_iprange_info *info = (const void *)match->data; + + if (info->flags & IPRANGE_SRC) { + printf("source IP range "); + if (info->flags & IPRANGE_SRC_INV) + printf("! "); + print_iprange(&info->src); + } + if (info->flags & IPRANGE_DST) { + printf("destination IP range "); + if (info->flags & IPRANGE_DST_INV) + printf("! "); + print_iprange(&info->dst); + } +} + +static void +iprange_mt4_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_iprange_mtinfo *info = (const void *)match->data; + + if (info->flags & IPRANGE_SRC) { + printf("source IP range "); + if (info->flags & IPRANGE_SRC_INV) + printf("! "); + /* + * ipaddr_to_numeric() uses a static buffer, so cannot + * combine the printf() calls. + */ + printf("%s", xtables_ipaddr_to_numeric(&info->src_min.in)); + printf("-%s ", xtables_ipaddr_to_numeric(&info->src_max.in)); + } + if (info->flags & IPRANGE_DST) { + printf("destination IP range "); + if (info->flags & IPRANGE_DST_INV) + printf("! "); + printf("%s", xtables_ipaddr_to_numeric(&info->dst_min.in)); + printf("-%s ", xtables_ipaddr_to_numeric(&info->dst_max.in)); + } +} + +static void +iprange_mt6_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_iprange_mtinfo *info = (const void *)match->data; + + if (info->flags & IPRANGE_SRC) { + printf("source IP range "); + if (info->flags & IPRANGE_SRC_INV) + printf("! "); + /* + * ipaddr_to_numeric() uses a static buffer, so cannot + * combine the printf() calls. + */ + printf("%s", xtables_ip6addr_to_numeric(&info->src_min.in6)); + printf("-%s ", xtables_ip6addr_to_numeric(&info->src_max.in6)); + } + if (info->flags & IPRANGE_DST) { + printf("destination IP range "); + if (info->flags & IPRANGE_DST_INV) + printf("! "); + printf("%s", xtables_ip6addr_to_numeric(&info->dst_min.in6)); + printf("-%s ", xtables_ip6addr_to_numeric(&info->dst_max.in6)); + } +} + +static void iprange_save(const void *ip, const struct xt_entry_match *match) +{ + const struct ipt_iprange_info *info = (const void *)match->data; + + if (info->flags & IPRANGE_SRC) { + if (info->flags & IPRANGE_SRC_INV) + printf("! "); + printf("--src-range "); + print_iprange(&info->src); + if (info->flags & IPRANGE_DST) + fputc(' ', stdout); + } + if (info->flags & IPRANGE_DST) { + if (info->flags & IPRANGE_DST_INV) + printf("! "); + printf("--dst-range "); + print_iprange(&info->dst); + } +} + +static void iprange_mt4_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_iprange_mtinfo *info = (const void *)match->data; + + if (info->flags & IPRANGE_SRC) { + if (info->flags & IPRANGE_SRC_INV) + printf("! "); + printf("--src-range %s", xtables_ipaddr_to_numeric(&info->src_min.in)); + printf("-%s ", xtables_ipaddr_to_numeric(&info->src_max.in)); + } + if (info->flags & IPRANGE_DST) { + if (info->flags & IPRANGE_DST_INV) + printf("! "); + printf("--dst-range %s", xtables_ipaddr_to_numeric(&info->dst_min.in)); + printf("-%s ", xtables_ipaddr_to_numeric(&info->dst_max.in)); + } +} + +static void iprange_mt6_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_iprange_mtinfo *info = (const void *)match->data; + + if (info->flags & IPRANGE_SRC) { + if (info->flags & IPRANGE_SRC_INV) + printf("! "); + printf("--src-range %s", xtables_ip6addr_to_numeric(&info->src_min.in6)); + printf("-%s ", xtables_ip6addr_to_numeric(&info->src_max.in6)); + } + if (info->flags & IPRANGE_DST) { + if (info->flags & IPRANGE_DST_INV) + printf("! "); + printf("--dst-range %s", xtables_ip6addr_to_numeric(&info->dst_min.in6)); + printf("-%s ", xtables_ip6addr_to_numeric(&info->dst_max.in6)); + } +} + +static struct xtables_match iprange_mt_reg[] = { + { + .version = XTABLES_VERSION, + .name = "iprange", + .revision = 0, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct ipt_iprange_info)), + .userspacesize = XT_ALIGN(sizeof(struct ipt_iprange_info)), + .help = iprange_mt_help, + .parse = iprange_parse, + .final_check = iprange_mt_check, + .print = iprange_print, + .save = iprange_save, + .extra_opts = iprange_mt_opts, + }, + { + .version = XTABLES_VERSION, + .name = "iprange", + .revision = 1, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)), + .userspacesize = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)), + .help = iprange_mt_help, + .parse = iprange_mt4_parse, + .final_check = iprange_mt_check, + .print = iprange_mt4_print, + .save = iprange_mt4_save, + .extra_opts = iprange_mt_opts, + }, + { + .version = XTABLES_VERSION, + .name = "iprange", + .revision = 1, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)), + .userspacesize = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)), + .help = iprange_mt_help, + .parse = iprange_mt6_parse, + .final_check = iprange_mt_check, + .print = iprange_mt6_print, + .save = iprange_mt6_save, + .extra_opts = iprange_mt_opts, + }, +}; + +void _init(void) +{ + xtables_register_matches(iprange_mt_reg, ARRAY_SIZE(iprange_mt_reg)); +} diff --git a/extensions/libxt_iprange.man b/extensions/libxt_iprange.man new file mode 100644 index 0000000..9f65de4 --- /dev/null +++ b/extensions/libxt_iprange.man @@ -0,0 +1,7 @@ +This matches on a given arbitrary range of IP addresses. +.TP +[\fB!\fR] \fB\-\-src\-range\fP \fIfrom\fP[\fB\-\fP\fIto\fP] +Match source IP in the specified range. +.TP +[\fB!\fR] \fB\-\-dst\-range\fP \fIfrom\fP[\fB\-\fP\fIto\fP] +Match destination IP in the specified range. diff --git a/extensions/libxt_length.c b/extensions/libxt_length.c new file mode 100644 index 0000000..96e8b6c --- /dev/null +++ b/extensions/libxt_length.c @@ -0,0 +1,133 @@ +/* Shared library add-on to iptables to add packet length matching support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/xt_length.h> + +static void length_help(void) +{ + printf( +"length match options:\n" +"[!] --length length[:length] Match packet length against value or range\n" +" of values (inclusive)\n"); +} + +static const struct option length_opts[] = { + { "length", 1, NULL, '1' }, + { .name = NULL } +}; + +static u_int16_t +parse_length(const char *s) +{ + unsigned int len; + + if (!xtables_strtoui(s, NULL, &len, 0, UINT32_MAX)) + xtables_error(PARAMETER_PROBLEM, "length invalid: \"%s\"\n", s); + else + return len; +} + +/* If a single value is provided, min and max are both set to the value */ +static void +parse_lengths(const char *s, struct xt_length_info *info) +{ + char *buffer; + char *cp; + + buffer = strdup(s); + if ((cp = strchr(buffer, ':')) == NULL) + info->min = info->max = parse_length(buffer); + else { + *cp = '\0'; + cp++; + + info->min = buffer[0] ? parse_length(buffer) : 0; + info->max = cp[0] ? parse_length(cp) : 0xFFFF; + } + free(buffer); + + if (info->min > info->max) + xtables_error(PARAMETER_PROBLEM, + "length min. range value `%u' greater than max. " + "range value `%u'", info->min, info->max); + +} + +static int +length_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_length_info *info = (struct xt_length_info *)(*match)->data; + + switch (c) { + case '1': + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "length: `--length' may only be " + "specified once"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_lengths(optarg, info); + if (invert) + info->invert = 1; + *flags = 1; + break; + + default: + return 0; + } + return 1; +} + +static void length_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "length: You must specify `--length'"); +} + +static void +length_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_length_info *info = (void *)match->data; + + printf("length %s", info->invert ? "!" : ""); + if (info->min == info->max) + printf("%u ", info->min); + else + printf("%u:%u ", info->min, info->max); +} + +static void length_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_length_info *info = (void *)match->data; + + printf("%s--length ", info->invert ? "! " : ""); + if (info->min == info->max) + printf("%u ", info->min); + else + printf("%u:%u ", info->min, info->max); +} + +static struct xtables_match length_match = { + .family = NFPROTO_UNSPEC, + .name = "length", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_length_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_length_info)), + .help = length_help, + .parse = length_parse, + .final_check = length_check, + .print = length_print, + .save = length_save, + .extra_opts = length_opts, +}; + +void _init(void) +{ + xtables_register_match(&length_match); +} diff --git a/extensions/libxt_length.man b/extensions/libxt_length.man new file mode 100644 index 0000000..07b6ea6 --- /dev/null +++ b/extensions/libxt_length.man @@ -0,0 +1,5 @@ +This module matches the length of the layer-3 payload (e.g. layer-4 packet) +of a packet against a specific value +or range of values. +.TP +[\fB!\fP] \fB\-\-length\fP \fIlength\fP[\fB:\fP\fIlength\fP] diff --git a/extensions/libxt_limit.c b/extensions/libxt_limit.c new file mode 100644 index 0000000..c836303 --- /dev/null +++ b/extensions/libxt_limit.c @@ -0,0 +1,177 @@ +/* Shared library add-on to iptables to add limit support. + * + * Jérôme de Vivie <devivie@info.enserb.u-bordeaux.fr> + * Hervé Eychenne <rv@wallfire.org> + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <stddef.h> +#include <linux/netfilter/x_tables.h> +/* For 64bit kernel / 32bit userspace */ +#include <linux/netfilter/xt_limit.h> + +#define XT_LIMIT_AVG "3/hour" +#define XT_LIMIT_BURST 5 + +static void limit_help(void) +{ + printf( +"limit match options:\n" +"--limit avg max average match rate: default "XT_LIMIT_AVG"\n" +" [Packets per second unless followed by \n" +" /sec /minute /hour /day postfixes]\n" +"--limit-burst number number to match in a burst, default %u\n", +XT_LIMIT_BURST); +} + +static const struct option limit_opts[] = { + { "limit", 1, NULL, '%' }, + { "limit-burst", 1, NULL, '$' }, + { .name = NULL } +}; + +static +int parse_rate(const char *rate, u_int32_t *val) +{ + const char *delim; + u_int32_t r; + u_int32_t mult = 1; /* Seconds by default. */ + + delim = strchr(rate, '/'); + if (delim) { + if (strlen(delim+1) == 0) + return 0; + + if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0) + mult = 1; + else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0) + mult = 60; + else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0) + mult = 60*60; + else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0) + mult = 24*60*60; + else + return 0; + } + r = atoi(rate); + if (!r) + return 0; + + /* This would get mapped to infinite (1/day is minimum they + can specify, so we're ok at that end). */ + if (r / mult > XT_LIMIT_SCALE) + xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate); + + *val = XT_LIMIT_SCALE * mult / r; + return 1; +} + +static void limit_init(struct xt_entry_match *m) +{ + struct xt_rateinfo *r = (struct xt_rateinfo *)m->data; + + parse_rate(XT_LIMIT_AVG, &r->avg); + r->burst = XT_LIMIT_BURST; + +} + +/* FIXME: handle overflow: + if (r->avg*r->burst/r->burst != r->avg) + xtables_error(PARAMETER_PROBLEM, + "Sorry: burst too large for that avg rate.\n"); +*/ + +static int +limit_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_rateinfo *r = (struct xt_rateinfo *)(*match)->data; + unsigned int num; + + switch(c) { + case '%': + if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) break; + if (!parse_rate(optarg, &r->avg)) + xtables_error(PARAMETER_PROBLEM, + "bad rate `%s'", optarg); + break; + + case '$': + if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) break; + if (!xtables_strtoui(optarg, NULL, &num, 0, 10000)) + xtables_error(PARAMETER_PROBLEM, + "bad --limit-burst `%s'", optarg); + r->burst = num; + break; + + default: + return 0; + } + + if (invert) + xtables_error(PARAMETER_PROBLEM, + "limit does not support invert"); + + return 1; +} + +static const struct rates +{ + const char *name; + u_int32_t mult; +} rates[] = { { "day", XT_LIMIT_SCALE*24*60*60 }, + { "hour", XT_LIMIT_SCALE*60*60 }, + { "min", XT_LIMIT_SCALE*60 }, + { "sec", XT_LIMIT_SCALE } }; + +static void print_rate(u_int32_t period) +{ + unsigned int i; + + for (i = 1; i < ARRAY_SIZE(rates); ++i) + if (period > rates[i].mult + || rates[i].mult/period < rates[i].mult%period) + break; + + printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name); +} + +static void +limit_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_rateinfo *r = (const void *)match->data; + printf("limit: avg "); print_rate(r->avg); + printf("burst %u ", r->burst); +} + +static void limit_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_rateinfo *r = (const void *)match->data; + + printf("--limit "); print_rate(r->avg); + if (r->burst != XT_LIMIT_BURST) + printf("--limit-burst %u ", r->burst); +} + +static struct xtables_match limit_match = { + .family = NFPROTO_UNSPEC, + .name = "limit", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_rateinfo)), + .userspacesize = offsetof(struct xt_rateinfo, prev), + .help = limit_help, + .init = limit_init, + .parse = limit_parse, + .print = limit_print, + .save = limit_save, + .extra_opts = limit_opts, +}; + +void _init(void) +{ + xtables_register_match(&limit_match); +} diff --git a/extensions/libxt_limit.man b/extensions/libxt_limit.man new file mode 100644 index 0000000..9f51ce3 --- /dev/null +++ b/extensions/libxt_limit.man @@ -0,0 +1,15 @@ +This module matches at a limited rate using a token bucket filter. +A rule using this extension will match until this limit is reached +(unless the `!' flag is used). It can be used in combination with the +.B LOG +target to give limited logging, for example. +.TP +\fB\-\-limit\fP \fIrate\fP[\fB/second\fP|\fB/minute\fP|\fB/hour\fP|\fB/day\fP] +Maximum average matching rate: specified as a number, with an optional +`/second', `/minute', `/hour', or `/day' suffix; the default is +3/hour. +.TP +\fB\-\-limit\-burst\fP \fInumber\fP +Maximum initial number of packets to match: this number gets +recharged by one every time the limit specified above is not reached, +up to this number; the default is 5. diff --git a/extensions/libxt_mac.c b/extensions/libxt_mac.c new file mode 100644 index 0000000..00996a0 --- /dev/null +++ b/extensions/libxt_mac.c @@ -0,0 +1,131 @@ +/* Shared library add-on to iptables to add MAC address support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#if defined(__GLIBC__) && __GLIBC__ == 2 +#include <net/ethernet.h> +#else +#include <linux/if_ether.h> +#endif +#include <xtables.h> +#include <linux/netfilter/xt_mac.h> + +static void mac_help(void) +{ + printf( +"mac match options:\n" +"[!] --mac-source XX:XX:XX:XX:XX:XX\n" +" Match source MAC address\n"); +} + +static const struct option mac_opts[] = { + { "mac-source", 1, NULL, '1' }, + { .name = NULL } +}; + +static void +parse_mac(const char *mac, struct xt_mac_info *info) +{ + unsigned int i = 0; + + if (strlen(mac) != ETH_ALEN*3-1) + xtables_error(PARAMETER_PROBLEM, "Bad mac address \"%s\"", mac); + + for (i = 0; i < ETH_ALEN; i++) { + long number; + char *end; + + number = strtol(mac + i*3, &end, 16); + + if (end == mac + i*3 + 2 + && number >= 0 + && number <= 255) + info->srcaddr[i] = number; + else + xtables_error(PARAMETER_PROBLEM, + "Bad mac address `%s'", mac); + } +} + +static int +mac_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_mac_info *macinfo = (struct xt_mac_info *)(*match)->data; + + switch (c) { + case '1': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_mac(optarg, macinfo); + if (invert) + macinfo->invert = 1; + *flags = 1; + break; + + default: + return 0; + } + + return 1; +} + +static void print_mac(const unsigned char macaddress[ETH_ALEN]) +{ + unsigned int i; + + printf("%02X", macaddress[0]); + for (i = 1; i < ETH_ALEN; i++) + printf(":%02X", macaddress[i]); + printf(" "); +} + +static void mac_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "You must specify `--mac-source'"); +} + +static void +mac_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_mac_info *info = (void *)match->data; + printf("MAC "); + + if (info->invert) + printf("! "); + + print_mac(info->srcaddr); +} + +static void mac_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_mac_info *info = (void *)match->data; + + if (info->invert) + printf("! "); + + printf("--mac-source "); + print_mac(info->srcaddr); +} + +static struct xtables_match mac_match = { + .family = NFPROTO_UNSPEC, + .name = "mac", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_mac_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_mac_info)), + .help = mac_help, + .parse = mac_parse, + .final_check = mac_check, + .print = mac_print, + .save = mac_save, + .extra_opts = mac_opts, +}; + +void _init(void) +{ + xtables_register_match(&mac_match); +} diff --git a/extensions/libxt_mac.man b/extensions/libxt_mac.man new file mode 100644 index 0000000..66072a2 --- /dev/null +++ b/extensions/libxt_mac.man @@ -0,0 +1,10 @@ +.TP +[\fB!\fP] \fB\-\-mac\-source\fP \fIaddress\fP +Match source MAC address. It must be of the form XX:XX:XX:XX:XX:XX. +Note that this only makes sense for packets coming from an Ethernet device +and entering the +.BR PREROUTING , +.B FORWARD +or +.B INPUT +chains. diff --git a/extensions/libxt_mark.c b/extensions/libxt_mark.c new file mode 100644 index 0000000..8013c9a --- /dev/null +++ b/extensions/libxt_mark.c @@ -0,0 +1,185 @@ +/* Shared library add-on to iptables to add NFMARK matching support. */ +#include <stdbool.h> +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/xt_mark.h> + +struct xt_mark_info { + unsigned long mark, mask; + u_int8_t invert; +}; + +enum { + F_MARK = 1 << 0, +}; + +static void mark_mt_help(void) +{ + printf( +"mark match options:\n" +"[!] --mark value[/mask] Match nfmark value with optional mask\n"); +} + +static const struct option mark_mt_opts[] = { + {.name = "mark", .has_arg = true, .val = '1'}, + { .name = NULL } +}; + +static int mark_mt_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_mark_mtinfo1 *info = (void *)(*match)->data; + unsigned int mark, mask = UINT32_MAX; + char *end; + + switch (c) { + case '1': /* --mark */ + xtables_param_act(XTF_ONLY_ONCE, "mark", "--mark", *flags & F_MARK); + if (!xtables_strtoui(optarg, &end, &mark, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "mark", "--mark", optarg); + if (*end == '/') + if (!xtables_strtoui(end + 1, &end, &mask, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "mark", "--mark", optarg); + if (*end != '\0') + xtables_param_act(XTF_BAD_VALUE, "mark", "--mark", optarg); + + if (invert) + info->invert = true; + info->mark = mark; + info->mask = mask; + *flags |= F_MARK; + return true; + } + return false; +} + +static int +mark_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_mark_info *markinfo = (struct xt_mark_info *)(*match)->data; + + switch (c) { + char *end; + case '1': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + markinfo->mark = strtoul(optarg, &end, 0); + if (*end == '/') { + markinfo->mask = strtoul(end+1, &end, 0); + } else + markinfo->mask = 0xffffffff; + if (*end != '\0' || end == optarg) + xtables_error(PARAMETER_PROBLEM, "Bad MARK value \"%s\"", optarg); + if (invert) + markinfo->invert = 1; + *flags = 1; + break; + + default: + return 0; + } + return 1; +} + +static void print_mark(unsigned int mark, unsigned int mask) +{ + if (mask != 0xffffffffU) + printf("0x%x/0x%x ", mark, mask); + else + printf("0x%x ", mark); +} + +static void mark_mt_check(unsigned int flags) +{ + if (flags == 0) + xtables_error(PARAMETER_PROBLEM, + "mark match: The --mark option is required"); +} + +static void +mark_mt_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_mark_mtinfo1 *info = (const void *)match->data; + + printf("mark match "); + if (info->invert) + printf("!"); + print_mark(info->mark, info->mask); +} + +static void +mark_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_mark_info *info = (const void *)match->data; + + printf("MARK match "); + + if (info->invert) + printf("!"); + + print_mark(info->mark, info->mask); +} + +static void mark_mt_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_mark_mtinfo1 *info = (const void *)match->data; + + if (info->invert) + printf("! "); + + printf("--mark "); + print_mark(info->mark, info->mask); +} + +static void +mark_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_mark_info *info = (const void *)match->data; + + if (info->invert) + printf("! "); + + printf("--mark "); + print_mark(info->mark, info->mask); +} + +static struct xtables_match mark_mt_reg[] = { + { + .family = NFPROTO_UNSPEC, + .name = "mark", + .revision = 0, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_mark_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_mark_info)), + .help = mark_mt_help, + .parse = mark_parse, + .final_check = mark_mt_check, + .print = mark_print, + .save = mark_save, + .extra_opts = mark_mt_opts, + }, + { + .version = XTABLES_VERSION, + .name = "mark", + .revision = 1, + .family = NFPROTO_UNSPEC, + .size = XT_ALIGN(sizeof(struct xt_mark_mtinfo1)), + .userspacesize = XT_ALIGN(sizeof(struct xt_mark_mtinfo1)), + .help = mark_mt_help, + .parse = mark_mt_parse, + .final_check = mark_mt_check, + .print = mark_mt_print, + .save = mark_mt_save, + .extra_opts = mark_mt_opts, + }, +}; + +void _init(void) +{ + xtables_register_matches(mark_mt_reg, ARRAY_SIZE(mark_mt_reg)); +} diff --git a/extensions/libxt_mark.man b/extensions/libxt_mark.man new file mode 100644 index 0000000..264b17d --- /dev/null +++ b/extensions/libxt_mark.man @@ -0,0 +1,9 @@ +This module matches the netfilter mark field associated with a packet +(which can be set using the +.B MARK +target below). +.TP +[\fB!\fP] \fB\-\-mark\fP \fIvalue\fP[\fB/\fP\fImask\fP] +Matches packets with the given unsigned mark value (if a \fImask\fP is +specified, this is logically ANDed with the \fImask\fP before the +comparison). diff --git a/extensions/libxt_multiport.c b/extensions/libxt_multiport.c new file mode 100644 index 0000000..e8a0dab --- /dev/null +++ b/extensions/libxt_multiport.c @@ -0,0 +1,575 @@ +/* Shared library add-on to iptables to add multiple TCP port support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <libiptc/libiptc.h> +#include <libiptc/libip6tc.h> +#include <limits.h> /* INT_MAX in ip_tables.h/ip6_tables.h */ +#include <linux/netfilter_ipv4/ip_tables.h> +#include <linux/netfilter_ipv6/ip6_tables.h> +#include <linux/netfilter/xt_multiport.h> + +/* Function which prints out usage message. */ +static void multiport_help(void) +{ + printf( +"multiport match options:\n" +" --source-ports port[,port,port...]\n" +" --sports ...\n" +" match source port(s)\n" +" --destination-ports port[,port,port...]\n" +" --dports ...\n" +" match destination port(s)\n" +" --ports port[,port,port]\n" +" match both source and destination port(s)\n" +" NOTE: this kernel does not support port ranges in multiport.\n"); +} + +static void multiport_help_v1(void) +{ + printf( +"multiport match options:\n" +"[!] --source-ports port[,port:port,port...]\n" +" --sports ...\n" +" match source port(s)\n" +"[!] --destination-ports port[,port:port,port...]\n" +" --dports ...\n" +" match destination port(s)\n" +"[!] --ports port[,port:port,port]\n" +" match both source and destination port(s)\n"); +} + +static const struct option multiport_opts[] = { + { "source-ports", 1, NULL, '1' }, + { "sports", 1, NULL, '1' }, /* synonym */ + { "destination-ports", 1, NULL, '2' }, + { "dports", 1, NULL, '2' }, /* synonym */ + { "ports", 1, NULL, '3' }, + { .name = NULL } +}; + +static char * +proto_to_name(u_int8_t proto) +{ + switch (proto) { + case IPPROTO_TCP: + return "tcp"; + case IPPROTO_UDP: + return "udp"; + case IPPROTO_UDPLITE: + return "udplite"; + case IPPROTO_SCTP: + return "sctp"; + case IPPROTO_DCCP: + return "dccp"; + default: + return NULL; + } +} + +static unsigned int +parse_multi_ports(const char *portstring, u_int16_t *ports, const char *proto) +{ + char *buffer, *cp, *next; + unsigned int i; + + buffer = strdup(portstring); + if (!buffer) xtables_error(OTHER_PROBLEM, "strdup failed"); + + for (cp=buffer, i=0; cp && i<XT_MULTI_PORTS; cp=next,i++) + { + next=strchr(cp, ','); + if (next) *next++='\0'; + ports[i] = xtables_parse_port(cp, proto); + } + if (cp) xtables_error(PARAMETER_PROBLEM, "too many ports specified"); + free(buffer); + return i; +} + +static void +parse_multi_ports_v1(const char *portstring, + struct xt_multiport_v1 *multiinfo, + const char *proto) +{ + char *buffer, *cp, *next, *range; + unsigned int i; + u_int16_t m; + + buffer = strdup(portstring); + if (!buffer) xtables_error(OTHER_PROBLEM, "strdup failed"); + + for (i=0; i<XT_MULTI_PORTS; i++) + multiinfo->pflags[i] = 0; + + for (cp=buffer, i=0; cp && i<XT_MULTI_PORTS; cp=next, i++) { + next=strchr(cp, ','); + if (next) *next++='\0'; + range = strchr(cp, ':'); + if (range) { + if (i == XT_MULTI_PORTS-1) + xtables_error(PARAMETER_PROBLEM, + "too many ports specified"); + *range++ = '\0'; + } + multiinfo->ports[i] = xtables_parse_port(cp, proto); + if (range) { + multiinfo->pflags[i] = 1; + multiinfo->ports[++i] = xtables_parse_port(range, proto); + if (multiinfo->ports[i-1] >= multiinfo->ports[i]) + xtables_error(PARAMETER_PROBLEM, + "invalid portrange specified"); + m <<= 1; + } + } + multiinfo->count = i; + if (cp) xtables_error(PARAMETER_PROBLEM, "too many ports specified"); + free(buffer); +} + +static const char * +check_proto(u_int16_t pnum, u_int8_t invflags) +{ + char *proto; + + if (invflags & XT_INV_PROTO) + xtables_error(PARAMETER_PROBLEM, + "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP"); + + if ((proto = proto_to_name(pnum)) != NULL) + return proto; + else if (!pnum) + xtables_error(PARAMETER_PROBLEM, + "multiport needs `-p tcp', `-p udp', `-p udplite', " + "`-p sctp' or `-p dccp'"); + else + xtables_error(PARAMETER_PROBLEM, + "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP"); +} + +/* Function which parses command options; returns true if it + ate an option */ +static int +__multiport_parse(int c, char **argv, int invert, unsigned int *flags, + struct xt_entry_match **match, u_int16_t pnum, + u_int8_t invflags) +{ + const char *proto; + struct xt_multiport *multiinfo + = (struct xt_multiport *)(*match)->data; + + switch (c) { + case '1': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + proto = check_proto(pnum, invflags); + multiinfo->count = parse_multi_ports(optarg, + multiinfo->ports, proto); + multiinfo->flags = XT_MULTIPORT_SOURCE; + break; + + case '2': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + proto = check_proto(pnum, invflags); + multiinfo->count = parse_multi_ports(optarg, + multiinfo->ports, proto); + multiinfo->flags = XT_MULTIPORT_DESTINATION; + break; + + case '3': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + proto = check_proto(pnum, invflags); + multiinfo->count = parse_multi_ports(optarg, + multiinfo->ports, proto); + multiinfo->flags = XT_MULTIPORT_EITHER; + break; + + default: + return 0; + } + + if (invert) + xtables_error(PARAMETER_PROBLEM, + "multiport does not support invert"); + + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "multiport can only have one option"); + *flags = 1; + return 1; +} + +static int +multiport_parse(int c, char **argv, int invert, unsigned int *flags, + const void *e, struct xt_entry_match **match) +{ + const struct ipt_entry *entry = e; + return __multiport_parse(c, argv, invert, flags, match, + entry->ip.proto, entry->ip.invflags); +} + +static int +multiport_parse6(int c, char **argv, int invert, unsigned int *flags, + const void *e, struct xt_entry_match **match) +{ + const struct ip6t_entry *entry = e; + return __multiport_parse(c, argv, invert, flags, match, + entry->ipv6.proto, entry->ipv6.invflags); +} + +static int +__multiport_parse_v1(int c, char **argv, int invert, unsigned int *flags, + struct xt_entry_match **match, u_int16_t pnum, + u_int8_t invflags) +{ + const char *proto; + struct xt_multiport_v1 *multiinfo + = (struct xt_multiport_v1 *)(*match)->data; + + switch (c) { + case '1': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + proto = check_proto(pnum, invflags); + parse_multi_ports_v1(optarg, multiinfo, proto); + multiinfo->flags = XT_MULTIPORT_SOURCE; + break; + + case '2': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + proto = check_proto(pnum, invflags); + parse_multi_ports_v1(optarg, multiinfo, proto); + multiinfo->flags = XT_MULTIPORT_DESTINATION; + break; + + case '3': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + proto = check_proto(pnum, invflags); + parse_multi_ports_v1(optarg, multiinfo, proto); + multiinfo->flags = XT_MULTIPORT_EITHER; + break; + + default: + return 0; + } + + if (invert) + multiinfo->invert = 1; + + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "multiport can only have one option"); + *flags = 1; + return 1; +} + +static int +multiport_parse_v1(int c, char **argv, int invert, unsigned int *flags, + const void *e, struct xt_entry_match **match) +{ + const struct ipt_entry *entry = e; + return __multiport_parse_v1(c, argv, invert, flags, match, + entry->ip.proto, entry->ip.invflags); +} + +static int +multiport_parse6_v1(int c, char **argv, int invert, unsigned int *flags, + const void *e, struct xt_entry_match **match) +{ + const struct ip6t_entry *entry = e; + return __multiport_parse_v1(c, argv, invert, flags, match, + entry->ipv6.proto, entry->ipv6.invflags); +} + +/* Final check; must specify something. */ +static void multiport_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, "multiport expection an option"); +} + +static char * +port_to_service(int port, u_int8_t proto) +{ + struct servent *service; + + if ((service = getservbyport(htons(port), proto_to_name(proto)))) + return service->s_name; + + return NULL; +} + +static void +print_port(u_int16_t port, u_int8_t protocol, int numeric) +{ + char *service; + + if (numeric || (service = port_to_service(port, protocol)) == NULL) + printf("%u", port); + else + printf("%s", service); +} + +/* Prints out the matchinfo. */ +static void +__multiport_print(const struct xt_entry_match *match, int numeric, + u_int16_t proto) +{ + const struct xt_multiport *multiinfo + = (const struct xt_multiport *)match->data; + unsigned int i; + + printf("multiport "); + + switch (multiinfo->flags) { + case XT_MULTIPORT_SOURCE: + printf("sports "); + break; + + case XT_MULTIPORT_DESTINATION: + printf("dports "); + break; + + case XT_MULTIPORT_EITHER: + printf("ports "); + break; + + default: + printf("ERROR "); + break; + } + + for (i=0; i < multiinfo->count; i++) { + printf("%s", i ? "," : ""); + print_port(multiinfo->ports[i], proto, numeric); + } + printf(" "); +} + +static void multiport_print(const void *ip_void, + const struct xt_entry_match *match, int numeric) +{ + const struct ipt_ip *ip = ip_void; + __multiport_print(match, numeric, ip->proto); +} + +static void multiport_print6(const void *ip_void, + const struct xt_entry_match *match, int numeric) +{ + const struct ip6t_ip6 *ip = ip_void; + __multiport_print(match, numeric, ip->proto); +} + +static void __multiport_print_v1(const struct xt_entry_match *match, + int numeric, u_int16_t proto) +{ + const struct xt_multiport_v1 *multiinfo + = (const struct xt_multiport_v1 *)match->data; + unsigned int i; + + printf("multiport "); + + switch (multiinfo->flags) { + case XT_MULTIPORT_SOURCE: + printf("sports "); + break; + + case XT_MULTIPORT_DESTINATION: + printf("dports "); + break; + + case XT_MULTIPORT_EITHER: + printf("ports "); + break; + + default: + printf("ERROR "); + break; + } + + if (multiinfo->invert) + printf("! "); + + for (i=0; i < multiinfo->count; i++) { + printf("%s", i ? "," : ""); + print_port(multiinfo->ports[i], proto, numeric); + if (multiinfo->pflags[i]) { + printf(":"); + print_port(multiinfo->ports[++i], proto, numeric); + } + } + printf(" "); +} + +static void multiport_print_v1(const void *ip_void, + const struct xt_entry_match *match, int numeric) +{ + const struct ipt_ip *ip = ip_void; + __multiport_print_v1(match, numeric, ip->proto); +} + +static void multiport_print6_v1(const void *ip_void, + const struct xt_entry_match *match, int numeric) +{ + const struct ip6t_ip6 *ip = ip_void; + __multiport_print_v1(match, numeric, ip->proto); +} + +/* Saves the union ipt_matchinfo in parsable form to stdout. */ +static void __multiport_save(const struct xt_entry_match *match, + u_int16_t proto) +{ + const struct xt_multiport *multiinfo + = (const struct xt_multiport *)match->data; + unsigned int i; + + switch (multiinfo->flags) { + case XT_MULTIPORT_SOURCE: + printf("--sports "); + break; + + case XT_MULTIPORT_DESTINATION: + printf("--dports "); + break; + + case XT_MULTIPORT_EITHER: + printf("--ports "); + break; + } + + for (i=0; i < multiinfo->count; i++) { + printf("%s", i ? "," : ""); + print_port(multiinfo->ports[i], proto, 1); + } + printf(" "); +} + +static void multiport_save(const void *ip_void, + const struct xt_entry_match *match) +{ + const struct ipt_ip *ip = ip_void; + __multiport_save(match, ip->proto); +} + +static void multiport_save6(const void *ip_void, + const struct xt_entry_match *match) +{ + const struct ip6t_ip6 *ip = ip_void; + __multiport_save(match, ip->proto); +} + +static void __multiport_save_v1(const struct xt_entry_match *match, + u_int16_t proto) +{ + const struct xt_multiport_v1 *multiinfo + = (const struct xt_multiport_v1 *)match->data; + unsigned int i; + + if (multiinfo->invert) + printf("! "); + + switch (multiinfo->flags) { + case XT_MULTIPORT_SOURCE: + printf("--sports "); + break; + + case XT_MULTIPORT_DESTINATION: + printf("--dports "); + break; + + case XT_MULTIPORT_EITHER: + printf("--ports "); + break; + } + + for (i=0; i < multiinfo->count; i++) { + printf("%s", i ? "," : ""); + print_port(multiinfo->ports[i], proto, 1); + if (multiinfo->pflags[i]) { + printf(":"); + print_port(multiinfo->ports[++i], proto, 1); + } + } + printf(" "); +} + +static void multiport_save_v1(const void *ip_void, + const struct xt_entry_match *match) +{ + const struct ipt_ip *ip = ip_void; + __multiport_save_v1(match, ip->proto); +} + +static void multiport_save6_v1(const void *ip_void, + const struct xt_entry_match *match) +{ + const struct ip6t_ip6 *ip = ip_void; + __multiport_save_v1(match, ip->proto); +} + +static struct xtables_match multiport_mt_reg[] = { + { + .family = NFPROTO_IPV4, + .name = "multiport", + .revision = 0, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_multiport)), + .userspacesize = XT_ALIGN(sizeof(struct xt_multiport)), + .help = multiport_help, + .parse = multiport_parse, + .final_check = multiport_check, + .print = multiport_print, + .save = multiport_save, + .extra_opts = multiport_opts, + }, + { + .family = NFPROTO_IPV6, + .name = "multiport", + .revision = 0, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_multiport)), + .userspacesize = XT_ALIGN(sizeof(struct xt_multiport)), + .help = multiport_help, + .parse = multiport_parse6, + .final_check = multiport_check, + .print = multiport_print6, + .save = multiport_save6, + .extra_opts = multiport_opts, + }, + { + .family = NFPROTO_IPV4, + .name = "multiport", + .version = XTABLES_VERSION, + .revision = 1, + .size = XT_ALIGN(sizeof(struct xt_multiport_v1)), + .userspacesize = XT_ALIGN(sizeof(struct xt_multiport_v1)), + .help = multiport_help_v1, + .parse = multiport_parse_v1, + .final_check = multiport_check, + .print = multiport_print_v1, + .save = multiport_save_v1, + .extra_opts = multiport_opts, + }, + { + .family = NFPROTO_IPV6, + .name = "multiport", + .version = XTABLES_VERSION, + .revision = 1, + .size = XT_ALIGN(sizeof(struct xt_multiport_v1)), + .userspacesize = XT_ALIGN(sizeof(struct xt_multiport_v1)), + .help = multiport_help_v1, + .parse = multiport_parse6_v1, + .final_check = multiport_check, + .print = multiport_print6_v1, + .save = multiport_save6_v1, + .extra_opts = multiport_opts, + }, +}; + +void +_init(void) +{ + xtables_register_matches(multiport_mt_reg, ARRAY_SIZE(multiport_mt_reg)); +} diff --git a/extensions/libxt_multiport.man b/extensions/libxt_multiport.man new file mode 100644 index 0000000..caf5c56 --- /dev/null +++ b/extensions/libxt_multiport.man @@ -0,0 +1,23 @@ +This module matches a set of source or destination ports. Up to 15 +ports can be specified. A port range (port:port) counts as two +ports. It can only be used in conjunction with +\fB\-p tcp\fP +or +\fB\-p udp\fP. +.TP +[\fB!\fP] \fB\-\-source\-ports\fP,\fB\-\-sports\fP \fIport\fP[\fB,\fP\fIport\fP|\fB,\fP\fIport\fP\fB:\fP\fIport\fP]... +Match if the source port is one of the given ports. The flag +\fB\-\-sports\fP +is a convenient alias for this option. Multiple ports or port ranges are +separated using a comma, and a port range is specified using a colon. +\fB53,1024:65535\fP would therefore match ports 53 and all from 1024 through +65535. +.TP +[\fB!\fP] \fB\-\-destination\-ports\fP,\fB\-\-dports\fP \fIport\fP[\fB,\fP\fIport\fP|\fB,\fP\fIport\fP\fB:\fP\fIport\fP]... +Match if the destination port is one of the given ports. The flag +\fB\-\-dports\fP +is a convenient alias for this option. +.TP +[\fB!\fP] \fB\-\-ports\fP \fIport\fP[\fB,\fP\fIport\fP|\fB,\fP\fIport\fP\fB:\fP\fIport\fP]... +Match if either the source or destination ports are equal to one of +the given ports. diff --git a/extensions/libxt_osf.c b/extensions/libxt_osf.c new file mode 100644 index 0000000..07b86e4 --- /dev/null +++ b/extensions/libxt_osf.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2003+ Evgeniy Polyakov <zbr@ioremap.net> + * + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * xtables interface for OS fingerprint matching module. + */ + +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <ctype.h> + +#include <linux/types.h> + +#include <xtables.h> + +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#include <linux/netfilter/xt_osf.h> + +static void osf_help(void) +{ + printf("OS fingerprint match options:\n" + "[!] --genre string Match a OS genre by passive fingerprinting.\n" + "--ttl level Use some TTL check extensions to determine OS:\n" + " 0 true ip and fingerprint TTL comparison. Works for LAN.\n" + " 1 check if ip TTL is less than fingerprint one. Works for global addresses.\n" + " 2 do not compare TTL at all. Allows to detect NMAP, but can produce false results.\n" + "--log level Log determined genres into dmesg even if they do not match desired one:\n" + " 0 log all matched or unknown signatures.\n" + " 1 log only first one.\n" + " 2 log all known matched signatures.\n" + ); +} + + +static const struct option osf_opts[] = { + { .name = "genre", .has_arg = true, .val = '1' }, + { .name = "ttl", .has_arg = true, .val = '2' }, + { .name = "log", .has_arg = true, .val = '3' }, + { .name = NULL } +}; + + +static void osf_parse_string(const char *s, struct xt_osf_info *info) +{ + if (strlen(s) < MAXGENRELEN) + strcpy(info->genre, s); + else + xtables_error(PARAMETER_PROBLEM, + "Genre string too long `%s' [%zd], max=%d", + s, strlen(s), MAXGENRELEN); +} + +static int osf_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, + struct xt_entry_match **match) +{ + struct xt_osf_info *info = (struct xt_osf_info *)(*match)->data; + + switch(c) { + case '1': /* --genre */ + if (*flags & XT_OSF_GENRE) + xtables_error(PARAMETER_PROBLEM, + "Can't specify multiple genre parameter"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + osf_parse_string(argv[optind-1], info); + if (invert) + info->flags |= XT_OSF_INVERT; + info->len=strlen(info->genre); + *flags |= XT_OSF_GENRE; + break; + case '2': /* --ttl */ + if (*flags & XT_OSF_TTL) + xtables_error(PARAMETER_PROBLEM, + "Can't specify multiple ttl parameter"); + *flags |= XT_OSF_TTL; + info->flags |= XT_OSF_TTL; + if (!xtables_strtoui(argv[optind-1], NULL, &info->ttl, 0, 2)) + xtables_error(PARAMETER_PROBLEM, "TTL parameter is too big"); + break; + case '3': /* --log */ + if (*flags & XT_OSF_LOG) + xtables_error(PARAMETER_PROBLEM, + "Can't specify multiple log parameter"); + *flags |= XT_OSF_LOG; + if (!xtables_strtoui(argv[optind-1], NULL, &info->loglevel, 0, 2)) + xtables_error(PARAMETER_PROBLEM, "Log level parameter is too big"); + info->flags |= XT_OSF_LOG; + break; + default: + return 0; + } + + return 1; +} + +static void osf_final_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "OS fingerprint match: You must specify `--genre'"); +} + +static void osf_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_osf_info *info = (const struct xt_osf_info*) match->data; + + printf("OS fingerprint match %s%s ", (info->flags & XT_OSF_INVERT) ? "! " : "", info->genre); +} + +static void osf_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_osf_info *info = (const struct xt_osf_info*) match->data; + + printf("--genre %s%s ", (info->flags & XT_OSF_INVERT) ? "! ": "", info->genre); +} + +static struct xtables_match osf_match = { + .name = "osf", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_osf_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_osf_info)), + .help = osf_help, + .parse = osf_parse, + .print = osf_print, + .final_check = osf_final_check, + .save = osf_save, + .extra_opts = osf_opts, + .family = NFPROTO_IPV4 +}; + +void _init(void) +{ + xtables_register_match(&osf_match); +} diff --git a/extensions/libxt_osf.man b/extensions/libxt_osf.man new file mode 100644 index 0000000..f3a85fb --- /dev/null +++ b/extensions/libxt_osf.man @@ -0,0 +1,45 @@ +The osf module does passive operating system fingerprinting. This modules +compares some data (Window Size, MSS, options and their order, TTL, DF, +and others) from packets with the SYN bit set. +.TP +[\fB!\fP] \fB\-\-genre\fP \fIstring\fP +Match an operating system genre by using a passive fingerprinting. +.TP +\fB\-\-ttl\fP \fIlevel\fP +Do additional TTL checks on the packet to determine the operating system. +\fIlevel\fP can be one of the following values: +.IP \(bu 4 +0 - True IP address and fingerprint TTL comparison. This generally works for +LANs. +.IP \(bu 4 +1 - Check if the IP header's TTL is less than the fingerprint one. Works for +globally-routable addresses. +.IP \(bu 4 +2 - Do not compare the TTL at all. +.TP +\fB\-\-log\fP \fIlevel\fP +Log determined genres into dmesg even if they do not match the desired one. +\fIlevel\fP can be one of the following values: +.IP \(bu 4 +0 - Log all matched or unknown signatures +.IP \(bu 4 +1 - Log only the first one +.IP \(bu 4 +2 - Log all known matched signatures +.PP +You may find something like this in syslog: +.PP +Windows [2000:SP3:Windows XP Pro SP1, 2000 SP3]: 11.22.33.55:4024 -> +11.22.33.44:139 hops=3 Linux [2.5-2.6:] : 1.2.3.4:42624 -> 1.2.3.5:22 hops=4 +.PP +OS fingerprints are loadable using the \fBnfnl_osf\fP program. To load +fingerprints from a file, use: +.PP +\fBnfnl_osf -f /usr/share/xtables/pf.os\fP +.PP +To remove them again, +.PP +\fBnfnl_osf -f /usr/share/xtables/pf.os -d\fP +.PP +The fingerprint database can be downlaoded from +http://www.openbsd.org/cgi-bin/cvsweb/src/etc/pf.os . diff --git a/extensions/libxt_owner.c b/extensions/libxt_owner.c new file mode 100644 index 0000000..b595d97 --- /dev/null +++ b/extensions/libxt_owner.c @@ -0,0 +1,614 @@ +/* + * libxt_owner - iptables addon for xt_owner + * + * Copyright © CC Computer Consultants GmbH, 2007 - 2008 + * Jan Engelhardt <jengelh@computergmbh.de> + */ +#include <getopt.h> +#include <grp.h> +#include <netdb.h> +#include <pwd.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#include <xtables.h> +#include <linux/netfilter/xt_owner.h> + +/* match and invert flags */ +enum { + IPT_OWNER_UID = 0x01, + IPT_OWNER_GID = 0x02, + IPT_OWNER_PID = 0x04, + IPT_OWNER_SID = 0x08, + IPT_OWNER_COMM = 0x10, + IP6T_OWNER_UID = IPT_OWNER_UID, + IP6T_OWNER_GID = IPT_OWNER_GID, + IP6T_OWNER_PID = IPT_OWNER_PID, + IP6T_OWNER_SID = IPT_OWNER_SID, + IP6T_OWNER_COMM = IPT_OWNER_COMM, +}; + +struct ipt_owner_info { + uid_t uid; + gid_t gid; + pid_t pid; + pid_t sid; + char comm[16]; + u_int8_t match, invert; /* flags */ +}; + +struct ip6t_owner_info { + uid_t uid; + gid_t gid; + pid_t pid; + pid_t sid; + char comm[16]; + u_int8_t match, invert; /* flags */ +}; + +/* + * Note: "UINT32_MAX - 1" is used in the code because -1 is a reserved + * UID/GID value anyway. + */ + +enum { + FLAG_UID_OWNER = 1 << 0, + FLAG_GID_OWNER = 1 << 1, + FLAG_SOCKET_EXISTS = 1 << 2, + FLAG_PID_OWNER = 1 << 3, + FLAG_SID_OWNER = 1 << 4, + FLAG_COMM = 1 << 5, +}; + +static void owner_mt_help_v0(void) +{ +#ifdef IPT_OWNER_COMM + printf( +"owner match options:\n" +"[!] --uid-owner userid Match local UID\n" +"[!] --gid-owner groupid Match local GID\n" +"[!] --pid-owner processid Match local PID\n" +"[!] --sid-owner sessionid Match local SID\n" +"[!] --cmd-owner name Match local command name\n" +"NOTE: PID, SID and command matching are broken on SMP\n"); +#else + printf( +"owner match options:\n" +"[!] --uid-owner userid Match local UID\n" +"[!] --gid-owner groupid Match local GID\n" +"[!] --pid-owner processid Match local PID\n" +"[!] --sid-owner sessionid Match local SID\n" +"NOTE: PID and SID matching are broken on SMP\n"); +#endif /* IPT_OWNER_COMM */ +} + +static void owner_mt6_help_v0(void) +{ + printf( +"owner match options:\n" +"[!] --uid-owner userid Match local UID\n" +"[!] --gid-owner groupid Match local GID\n" +"[!] --pid-owner processid Match local PID\n" +"[!] --sid-owner sessionid Match local SID\n" +"NOTE: PID and SID matching are broken on SMP\n"); +} + +static void owner_mt_help(void) +{ + printf( +"owner match options:\n" +"[!] --uid-owner userid[-userid] Match local UID\n" +"[!] --gid-owner groupid[-groupid] Match local GID\n" +"[!] --socket-exists Match if socket exists\n"); +} + +static const struct option owner_mt_opts_v0[] = { + {.name = "uid-owner", .has_arg = true, .val = 'u'}, + {.name = "gid-owner", .has_arg = true, .val = 'g'}, + {.name = "pid-owner", .has_arg = true, .val = 'p'}, + {.name = "sid-owner", .has_arg = true, .val = 's'}, +#ifdef IPT_OWNER_COMM + {.name = "cmd-owner", .has_arg = true, .val = 'c'}, +#endif + { .name = NULL } +}; + +static const struct option owner_mt6_opts_v0[] = { + {.name = "uid-owner", .has_arg = true, .val = 'u'}, + {.name = "gid-owner", .has_arg = true, .val = 'g'}, + {.name = "pid-owner", .has_arg = true, .val = 'p'}, + {.name = "sid-owner", .has_arg = true, .val = 's'}, + { .name = NULL } +}; + +static const struct option owner_mt_opts[] = { + {.name = "uid-owner", .has_arg = true, .val = 'u'}, + {.name = "gid-owner", .has_arg = true, .val = 'g'}, + {.name = "socket-exists", .has_arg = false, .val = 'k'}, + { .name = NULL } +}; + +static int +owner_mt_parse_v0(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ipt_owner_info *info = (void *)(*match)->data; + struct passwd *pwd; + struct group *grp; + unsigned int id; + + switch (c) { + case 'u': + xtables_param_act(XTF_ONLY_ONCE, "owner", "--uid-owner", *flags & FLAG_UID_OWNER); + if ((pwd = getpwnam(optarg)) != NULL) + id = pwd->pw_uid; + else if (!xtables_strtoui(optarg, NULL, &id, 0, UINT32_MAX - 1)) + xtables_param_act(XTF_BAD_VALUE, "owner", "--uid-owner", optarg); + if (invert) + info->invert |= IPT_OWNER_UID; + info->match |= IPT_OWNER_UID; + info->uid = id; + *flags |= FLAG_UID_OWNER; + return true; + + case 'g': + xtables_param_act(XTF_ONLY_ONCE, "owner", "--gid-owner", *flags & FLAG_GID_OWNER); + if ((grp = getgrnam(optarg)) != NULL) + id = grp->gr_gid; + else if (!xtables_strtoui(optarg, NULL, &id, 0, UINT32_MAX - 1)) + xtables_param_act(XTF_BAD_VALUE, "owner", "--gid-owner", optarg); + if (invert) + info->invert |= IPT_OWNER_GID; + info->match |= IPT_OWNER_GID; + info->gid = id; + *flags |= FLAG_GID_OWNER; + return true; + + case 'p': + xtables_param_act(XTF_ONLY_ONCE, "owner", "--pid-owner", *flags & FLAG_PID_OWNER); + if (!xtables_strtoui(optarg, NULL, &id, 0, INT_MAX)) + xtables_param_act(XTF_BAD_VALUE, "owner", "--pid-owner", optarg); + if (invert) + info->invert |= IPT_OWNER_PID; + info->match |= IPT_OWNER_PID; + info->pid = id; + *flags |= FLAG_PID_OWNER; + return true; + + case 's': + xtables_param_act(XTF_ONLY_ONCE, "owner", "--sid-owner", *flags & FLAG_SID_OWNER); + if (!xtables_strtoui(optarg, NULL, &id, 0, INT_MAX)) + xtables_param_act(XTF_BAD_VALUE, "owner", "--sid-value", optarg); + if (invert) + info->invert |= IPT_OWNER_SID; + info->match |= IPT_OWNER_SID; + info->sid = id; + *flags |= FLAG_SID_OWNER; + return true; + +#ifdef IPT_OWNER_COMM + case 'c': + xtables_param_act(XTF_ONLY_ONCE, "owner", "--cmd-owner", *flags & FLAG_COMM); + if (strlen(optarg) > sizeof(info->comm)) + xtables_error(PARAMETER_PROBLEM, "owner match: command " + "\"%s\" too long, max. %zu characters", + optarg, sizeof(info->comm)); + + info->comm[sizeof(info->comm)-1] = '\0'; + strncpy(info->comm, optarg, sizeof(info->comm)); + + if (invert) + info->invert |= IPT_OWNER_COMM; + info->match |= IPT_OWNER_COMM; + *flags |= FLAG_COMM; + return true; +#endif + } + return false; +} + +static int +owner_mt6_parse_v0(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ip6t_owner_info *info = (void *)(*match)->data; + struct passwd *pwd; + struct group *grp; + unsigned int id; + + switch (c) { + case 'u': + xtables_param_act(XTF_ONLY_ONCE, "owner", "--uid-owner", + *flags & FLAG_UID_OWNER); + if ((pwd = getpwnam(optarg)) != NULL) + id = pwd->pw_uid; + else if (!xtables_strtoui(optarg, NULL, &id, 0, UINT32_MAX - 1)) + xtables_param_act(XTF_BAD_VALUE, "owner", "--uid-owner", optarg); + if (invert) + info->invert |= IP6T_OWNER_UID; + info->match |= IP6T_OWNER_UID; + info->uid = id; + *flags |= FLAG_UID_OWNER; + return true; + + case 'g': + xtables_param_act(XTF_ONLY_ONCE, "owner", "--gid-owner", + *flags & FLAG_GID_OWNER); + if ((grp = getgrnam(optarg)) != NULL) + id = grp->gr_gid; + else if (!xtables_strtoui(optarg, NULL, &id, 0, UINT32_MAX - 1)) + xtables_param_act(XTF_BAD_VALUE, "owner", "--gid-owner", optarg); + if (invert) + info->invert |= IP6T_OWNER_GID; + info->match |= IP6T_OWNER_GID; + info->gid = id; + *flags |= FLAG_GID_OWNER; + return true; + + case 'p': + xtables_param_act(XTF_ONLY_ONCE, "owner", "--pid-owner", + *flags & FLAG_PID_OWNER); + if (!xtables_strtoui(optarg, NULL, &id, 0, INT_MAX)) + xtables_param_act(XTF_BAD_VALUE, "owner", "--pid-owner", optarg); + if (invert) + info->invert |= IP6T_OWNER_PID; + info->match |= IP6T_OWNER_PID; + info->pid = id; + *flags |= FLAG_PID_OWNER; + return true; + + case 's': + xtables_param_act(XTF_ONLY_ONCE, "owner", "--sid-owner", + *flags & FLAG_SID_OWNER); + if (!xtables_strtoui(optarg, NULL, &id, 0, INT_MAX)) + xtables_param_act(XTF_BAD_VALUE, "owner", "--sid-owner", optarg); + if (invert) + info->invert |= IP6T_OWNER_SID; + info->match |= IP6T_OWNER_SID; + info->sid = id; + *flags |= FLAG_SID_OWNER; + return true; + } + return false; +} + +static void owner_parse_range(const char *s, unsigned int *from, + unsigned int *to, const char *opt) +{ + char *end; + + /* -1 is reversed, so the max is one less than that. */ + if (!xtables_strtoui(s, &end, from, 0, UINT32_MAX - 1)) + xtables_param_act(XTF_BAD_VALUE, "owner", opt, s); + *to = *from; + if (*end == '-' || *end == ':') + if (!xtables_strtoui(end + 1, &end, to, 0, UINT32_MAX - 1)) + xtables_param_act(XTF_BAD_VALUE, "owner", opt, s); + if (*end != '\0') + xtables_param_act(XTF_BAD_VALUE, "owner", opt, s); +} + +static int owner_mt_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_owner_match_info *info = (void *)(*match)->data; + struct passwd *pwd; + struct group *grp; + unsigned int from, to; + + switch (c) { + case 'u': + xtables_param_act(XTF_ONLY_ONCE, "owner", "--uid-owner", + *flags & FLAG_UID_OWNER); + if ((pwd = getpwnam(optarg)) != NULL) + from = to = pwd->pw_uid; + else + owner_parse_range(optarg, &from, &to, "--uid-owner"); + if (invert) + info->invert |= XT_OWNER_UID; + info->match |= XT_OWNER_UID; + info->uid_min = from; + info->uid_max = to; + *flags |= FLAG_UID_OWNER; + return true; + + case 'g': + xtables_param_act(XTF_ONLY_ONCE, "owner", "--gid-owner", + *flags & FLAG_GID_OWNER); + if ((grp = getgrnam(optarg)) != NULL) + from = to = grp->gr_gid; + else + owner_parse_range(optarg, &from, &to, "--gid-owner"); + if (invert) + info->invert |= XT_OWNER_GID; + info->match |= XT_OWNER_GID; + info->gid_min = from; + info->gid_max = to; + *flags |= FLAG_GID_OWNER; + return true; + + case 'k': + xtables_param_act(XTF_ONLY_ONCE, "owner", "--socket-exists", + *flags & FLAG_SOCKET_EXISTS); + if (invert) + info->invert |= XT_OWNER_SOCKET; + info->match |= XT_OWNER_SOCKET; + *flags |= FLAG_SOCKET_EXISTS; + return true; + + } + return false; +} + +static void owner_mt_check(unsigned int flags) +{ + if (flags == 0) + xtables_error(PARAMETER_PROBLEM, "owner: At least one of " + "--uid-owner, --gid-owner or --socket-exists " + "is required"); +} + +static void +owner_mt_print_item_v0(const struct ipt_owner_info *info, const char *label, + u_int8_t flag, bool numeric) +{ + if (!(info->match & flag)) + return; + if (info->invert & flag) + printf("! "); + printf("%s ", label); + + switch (info->match & flag) { + case IPT_OWNER_UID: + if (!numeric) { + struct passwd *pwd = getpwuid(info->uid); + + if (pwd != NULL && pwd->pw_name != NULL) { + printf("%s ", pwd->pw_name); + break; + } + } + printf("%u ", (unsigned int)info->uid); + break; + + case IPT_OWNER_GID: + if (!numeric) { + struct group *grp = getgrgid(info->gid); + + if (grp != NULL && grp->gr_name != NULL) { + printf("%s ", grp->gr_name); + break; + } + } + printf("%u ", (unsigned int)info->gid); + break; + + case IPT_OWNER_PID: + printf("%u ", (unsigned int)info->pid); + break; + + case IPT_OWNER_SID: + printf("%u ", (unsigned int)info->sid); + break; + +#ifdef IPT_OWNER_COMM + case IPT_OWNER_COMM: + printf("%.*s ", (int)sizeof(info->comm), info->comm); + break; +#endif + } +} + +static void +owner_mt6_print_item_v0(const struct ip6t_owner_info *info, const char *label, + u_int8_t flag, bool numeric) +{ + if (!(info->match & flag)) + return; + if (info->invert & flag) + printf("! "); + printf("%s ", label); + + switch (info->match & flag) { + case IP6T_OWNER_UID: + if (!numeric) { + struct passwd *pwd = getpwuid(info->uid); + + if (pwd != NULL && pwd->pw_name != NULL) { + printf("%s ", pwd->pw_name); + break; + } + } + printf("%u ", (unsigned int)info->uid); + break; + + case IP6T_OWNER_GID: + if (!numeric) { + struct group *grp = getgrgid(info->gid); + + if (grp != NULL && grp->gr_name != NULL) { + printf("%s ", grp->gr_name); + break; + } + } + printf("%u ", (unsigned int)info->gid); + break; + + case IP6T_OWNER_PID: + printf("%u ", (unsigned int)info->pid); + break; + + case IP6T_OWNER_SID: + printf("%u ", (unsigned int)info->sid); + break; + } +} + +static void +owner_mt_print_item(const struct xt_owner_match_info *info, const char *label, + u_int8_t flag, bool numeric) +{ + if (!(info->match & flag)) + return; + if (info->invert & flag) + printf("! "); + printf("%s ", label); + + switch (info->match & flag) { + case XT_OWNER_UID: + if (info->uid_min != info->uid_max) { + printf("%u-%u ", (unsigned int)info->uid_min, + (unsigned int)info->uid_max); + break; + } else if (!numeric) { + const struct passwd *pwd = getpwuid(info->uid_min); + + if (pwd != NULL && pwd->pw_name != NULL) { + printf("%s ", pwd->pw_name); + break; + } + } + printf("%u ", (unsigned int)info->uid_min); + break; + + case XT_OWNER_GID: + if (info->gid_min != info->gid_max) { + printf("%u-%u ", (unsigned int)info->gid_min, + (unsigned int)info->gid_max); + break; + } else if (!numeric) { + const struct group *grp = getgrgid(info->gid_min); + + if (grp != NULL && grp->gr_name != NULL) { + printf("%s ", grp->gr_name); + break; + } + } + printf("%u ", (unsigned int)info->gid_min); + break; + } +} + +static void +owner_mt_print_v0(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ipt_owner_info *info = (void *)match->data; + + owner_mt_print_item_v0(info, "owner UID match", IPT_OWNER_UID, numeric); + owner_mt_print_item_v0(info, "owner GID match", IPT_OWNER_GID, numeric); + owner_mt_print_item_v0(info, "owner PID match", IPT_OWNER_PID, numeric); + owner_mt_print_item_v0(info, "owner SID match", IPT_OWNER_SID, numeric); +#ifdef IPT_OWNER_COMM + owner_mt_print_item_v0(info, "owner CMD match", IPT_OWNER_COMM, numeric); +#endif +} + +static void +owner_mt6_print_v0(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ip6t_owner_info *info = (void *)match->data; + + owner_mt6_print_item_v0(info, "owner UID match", IPT_OWNER_UID, numeric); + owner_mt6_print_item_v0(info, "owner GID match", IPT_OWNER_GID, numeric); + owner_mt6_print_item_v0(info, "owner PID match", IPT_OWNER_PID, numeric); + owner_mt6_print_item_v0(info, "owner SID match", IPT_OWNER_SID, numeric); +} + +static void owner_mt_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_owner_match_info *info = (void *)match->data; + + owner_mt_print_item(info, "owner socket exists", XT_OWNER_SOCKET, numeric); + owner_mt_print_item(info, "owner UID match", XT_OWNER_UID, numeric); + owner_mt_print_item(info, "owner GID match", XT_OWNER_GID, numeric); +} + +static void +owner_mt_save_v0(const void *ip, const struct xt_entry_match *match) +{ + const struct ipt_owner_info *info = (void *)match->data; + + owner_mt_print_item_v0(info, "--uid-owner", IPT_OWNER_UID, true); + owner_mt_print_item_v0(info, "--gid-owner", IPT_OWNER_GID, true); + owner_mt_print_item_v0(info, "--pid-owner", IPT_OWNER_PID, true); + owner_mt_print_item_v0(info, "--sid-owner", IPT_OWNER_SID, true); +#ifdef IPT_OWNER_COMM + owner_mt_print_item_v0(info, "--cmd-owner", IPT_OWNER_COMM, true); +#endif +} + +static void +owner_mt6_save_v0(const void *ip, const struct xt_entry_match *match) +{ + const struct ip6t_owner_info *info = (void *)match->data; + + owner_mt6_print_item_v0(info, "--uid-owner", IPT_OWNER_UID, true); + owner_mt6_print_item_v0(info, "--gid-owner", IPT_OWNER_GID, true); + owner_mt6_print_item_v0(info, "--pid-owner", IPT_OWNER_PID, true); + owner_mt6_print_item_v0(info, "--sid-owner", IPT_OWNER_SID, true); +} + +static void owner_mt_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_owner_match_info *info = (void *)match->data; + + owner_mt_print_item(info, "--socket-exists", XT_OWNER_SOCKET, false); + owner_mt_print_item(info, "--uid-owner", XT_OWNER_UID, false); + owner_mt_print_item(info, "--gid-owner", XT_OWNER_GID, false); +} + +static struct xtables_match owner_mt_reg[] = { + { + .version = XTABLES_VERSION, + .name = "owner", + .revision = 0, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct ipt_owner_info)), + .userspacesize = XT_ALIGN(sizeof(struct ipt_owner_info)), + .help = owner_mt_help_v0, + .parse = owner_mt_parse_v0, + .final_check = owner_mt_check, + .print = owner_mt_print_v0, + .save = owner_mt_save_v0, + .extra_opts = owner_mt_opts_v0, + }, + { + .version = XTABLES_VERSION, + .name = "owner", + .revision = 0, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct ip6t_owner_info)), + .userspacesize = XT_ALIGN(sizeof(struct ip6t_owner_info)), + .help = owner_mt6_help_v0, + .parse = owner_mt6_parse_v0, + .final_check = owner_mt_check, + .print = owner_mt6_print_v0, + .save = owner_mt6_save_v0, + .extra_opts = owner_mt6_opts_v0, + }, + { + .version = XTABLES_VERSION, + .name = "owner", + .revision = 1, + .family = NFPROTO_UNSPEC, + .size = XT_ALIGN(sizeof(struct xt_owner_match_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_owner_match_info)), + .help = owner_mt_help, + .parse = owner_mt_parse, + .final_check = owner_mt_check, + .print = owner_mt_print, + .save = owner_mt_save, + .extra_opts = owner_mt_opts, + }, +}; + +void _init(void) +{ + xtables_register_matches(owner_mt_reg, ARRAY_SIZE(owner_mt_reg)); +} diff --git a/extensions/libxt_owner.man b/extensions/libxt_owner.man new file mode 100644 index 0000000..49b58ce --- /dev/null +++ b/extensions/libxt_owner.man @@ -0,0 +1,19 @@ +This module attempts to match various characteristics of the packet creator, +for locally generated packets. This match is only valid in the OUTPUT and +POSTROUTING chains. Forwarded packets do not have any socket associated with +them. Packets from kernel threads do have a socket, but usually no owner. +.TP +[\fB!\fP] \fB\-\-uid\-owner\fP \fIusername\fP +.TP +[\fB!\fP] \fB\-\-uid\-owner\fP \fIuserid\fP[\fB\-\fP\fIuserid\fP] +Matches if the packet socket's file structure (if it has one) is owned by the +given user. You may also specify a numerical UID, or an UID range. +.TP +[\fB!\fP] \fB\-\-gid\-owner\fP \fIgroupname\fP +.TP +[\fB!\fP] \fB\-\-gid\-owner\fP \fIgroupid\fP[\fB\-\fP\fIgroupid\fP] +Matches if the packet socket's file structure is owned by the given group. +You may also specify a numerical GID, or a GID range. +.TP +[\fB!\fP] \fB\-\-socket\-exists\fP +Matches if the packet is associated with a socket. diff --git a/extensions/libxt_physdev.c b/extensions/libxt_physdev.c new file mode 100644 index 0000000..5382ab6 --- /dev/null +++ b/extensions/libxt_physdev.c @@ -0,0 +1,180 @@ +/* Shared library add-on to iptables to add bridge port matching support. */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <ctype.h> +#include <xtables.h> +#include <linux/netfilter/xt_physdev.h> +#if defined(__GLIBC__) && __GLIBC__ == 2 +#include <net/ethernet.h> +#else +#include <linux/if_ether.h> +#endif + +static void physdev_help(void) +{ + printf( +"physdev match options:\n" +" [!] --physdev-in inputname[+] bridge port name ([+] for wildcard)\n" +" [!] --physdev-out outputname[+] bridge port name ([+] for wildcard)\n" +" [!] --physdev-is-in arrived on a bridge device\n" +" [!] --physdev-is-out will leave on a bridge device\n" +" [!] --physdev-is-bridged it's a bridged packet\n"); +} + +static const struct option physdev_opts[] = { + { "physdev-in", 1, NULL, '1' }, + { "physdev-out", 1, NULL, '2' }, + { "physdev-is-in", 0, NULL, '3' }, + { "physdev-is-out", 0, NULL, '4' }, + { "physdev-is-bridged", 0, NULL, '5' }, + { .name = NULL } +}; + +static int +physdev_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_physdev_info *info = + (struct xt_physdev_info*)(*match)->data; + + switch (c) { + case '1': + if (*flags & XT_PHYSDEV_OP_IN) + goto multiple_use; + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + xtables_parse_interface(optarg, info->physindev, + (unsigned char *)info->in_mask); + if (invert) + info->invert |= XT_PHYSDEV_OP_IN; + info->bitmask |= XT_PHYSDEV_OP_IN; + *flags |= XT_PHYSDEV_OP_IN; + break; + + case '2': + if (*flags & XT_PHYSDEV_OP_OUT) + goto multiple_use; + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + xtables_parse_interface(optarg, info->physoutdev, + (unsigned char *)info->out_mask); + if (invert) + info->invert |= XT_PHYSDEV_OP_OUT; + info->bitmask |= XT_PHYSDEV_OP_OUT; + *flags |= XT_PHYSDEV_OP_OUT; + break; + + case '3': + if (*flags & XT_PHYSDEV_OP_ISIN) + goto multiple_use; + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + info->bitmask |= XT_PHYSDEV_OP_ISIN; + if (invert) + info->invert |= XT_PHYSDEV_OP_ISIN; + *flags |= XT_PHYSDEV_OP_ISIN; + break; + + case '4': + if (*flags & XT_PHYSDEV_OP_ISOUT) + goto multiple_use; + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + info->bitmask |= XT_PHYSDEV_OP_ISOUT; + if (invert) + info->invert |= XT_PHYSDEV_OP_ISOUT; + *flags |= XT_PHYSDEV_OP_ISOUT; + break; + + case '5': + if (*flags & XT_PHYSDEV_OP_BRIDGED) + goto multiple_use; + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) + info->invert |= XT_PHYSDEV_OP_BRIDGED; + *flags |= XT_PHYSDEV_OP_BRIDGED; + info->bitmask |= XT_PHYSDEV_OP_BRIDGED; + break; + + default: + return 0; + } + + return 1; +multiple_use: + xtables_error(PARAMETER_PROBLEM, + "multiple use of the same physdev option is not allowed"); + +} + +static void physdev_check(unsigned int flags) +{ + if (flags == 0) + xtables_error(PARAMETER_PROBLEM, "PHYSDEV: no physdev option specified"); +} + +static void +physdev_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_physdev_info *info = (const void *)match->data; + + printf("PHYSDEV match"); + if (info->bitmask & XT_PHYSDEV_OP_ISIN) + printf("%s --physdev-is-in", + info->invert & XT_PHYSDEV_OP_ISIN ? " !":""); + if (info->bitmask & XT_PHYSDEV_OP_IN) + printf("%s --physdev-in %s", + (info->invert & XT_PHYSDEV_OP_IN) ? " !":"", info->physindev); + + if (info->bitmask & XT_PHYSDEV_OP_ISOUT) + printf("%s --physdev-is-out", + info->invert & XT_PHYSDEV_OP_ISOUT ? " !":""); + if (info->bitmask & XT_PHYSDEV_OP_OUT) + printf("%s --physdev-out %s", + (info->invert & XT_PHYSDEV_OP_OUT) ? " !":"", info->physoutdev); + if (info->bitmask & XT_PHYSDEV_OP_BRIDGED) + printf("%s --physdev-is-bridged", + info->invert & XT_PHYSDEV_OP_BRIDGED ? " !":""); + printf(" "); +} + +static void physdev_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_physdev_info *info = (const void *)match->data; + + if (info->bitmask & XT_PHYSDEV_OP_ISIN) + printf("%s--physdev-is-in ", + (info->invert & XT_PHYSDEV_OP_ISIN) ? "! " : ""); + if (info->bitmask & XT_PHYSDEV_OP_IN) + printf("%s--physdev-in %s ", + (info->invert & XT_PHYSDEV_OP_IN) ? "! " : "", + info->physindev); + + if (info->bitmask & XT_PHYSDEV_OP_ISOUT) + printf("%s--physdev-is-out ", + (info->invert & XT_PHYSDEV_OP_ISOUT) ? "! " : ""); + if (info->bitmask & XT_PHYSDEV_OP_OUT) + printf("%s--physdev-out %s ", + (info->invert & XT_PHYSDEV_OP_OUT) ? "! " : "", + info->physoutdev); + if (info->bitmask & XT_PHYSDEV_OP_BRIDGED) + printf("%s--physdev-is-bridged ", + (info->invert & XT_PHYSDEV_OP_BRIDGED) ? "! " : ""); +} + +static struct xtables_match physdev_match = { + .family = NFPROTO_UNSPEC, + .name = "physdev", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_physdev_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_physdev_info)), + .help = physdev_help, + .parse = physdev_parse, + .final_check = physdev_check, + .print = physdev_print, + .save = physdev_save, + .extra_opts = physdev_opts, +}; + +void _init(void) +{ + xtables_register_match(&physdev_match); +} diff --git a/extensions/libxt_physdev.man b/extensions/libxt_physdev.man new file mode 100644 index 0000000..53beb2e --- /dev/null +++ b/extensions/libxt_physdev.man @@ -0,0 +1,42 @@ +This module matches on the bridge port input and output devices enslaved +to a bridge device. This module is a part of the infrastructure that enables +a transparent bridging IP firewall and is only useful for kernel versions +above version 2.5.44. +.TP +[\fB!\fP] \fB\-\-physdev\-in\fP \fIname\fP +Name of a bridge port via which a packet is received (only for +packets entering the +.BR INPUT , +.B FORWARD +and +.B PREROUTING +chains). If the interface name ends in a "+", then any +interface which begins with this name will match. If the packet didn't arrive +through a bridge device, this packet won't match this option, unless '!' is used. +.TP +[\fB!\fP] \fB\-\-physdev\-out\fP \fIname\fP +Name of a bridge port via which a packet is going to be sent (for packets +entering the +.BR FORWARD , +.B OUTPUT +and +.B POSTROUTING +chains). If the interface name ends in a "+", then any +interface which begins with this name will match. Note that in the +.BR nat " and " mangle +.B OUTPUT +chains one cannot match on the bridge output port, however one can in the +.B "filter OUTPUT" +chain. If the packet won't leave by a bridge device or if it is yet unknown what +the output device will be, then the packet won't match this option, +unless '!' is used. +.TP +[\fB!\fP] \fB\-\-physdev\-is\-in\fP +Matches if the packet has entered through a bridge interface. +.TP +[\fB!\fP] \fB\-\-physdev\-is\-out\fP +Matches if the packet will leave through a bridge interface. +.TP +[\fB!\fP] \fB\-\-physdev\-is\-bridged\fP +Matches if the packet is being bridged and therefore is not being routed. +This is only useful in the FORWARD and POSTROUTING chains. diff --git a/extensions/libxt_pkttype.c b/extensions/libxt_pkttype.c new file mode 100644 index 0000000..8de1853 --- /dev/null +++ b/extensions/libxt_pkttype.c @@ -0,0 +1,159 @@ +/* + * Shared library add-on to iptables to match + * packets by their type (BROADCAST, UNICAST, MULTICAST). + * + * Michal Ludvig <michal@logix.cz> + */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#if defined(__GLIBC__) && __GLIBC__ == 2 +#include <net/ethernet.h> +#else +#include <linux/if_ether.h> +#endif +#include <xtables.h> +#define __aligned_u64 __u64 __attribute__((aligned(8))) +#include <linux/if_packet.h> +#include <linux/netfilter/xt_pkttype.h> + +#define PKTTYPE_VERSION "0.1" + +struct pkttypes { + const char *name; + unsigned char pkttype; + unsigned char printhelp; + const char *help; +}; + +static const struct pkttypes supported_types[] = { + {"unicast", PACKET_HOST, 1, "to us"}, + {"broadcast", PACKET_BROADCAST, 1, "to all"}, + {"multicast", PACKET_MULTICAST, 1, "to group"}, +/* + {"otherhost", PACKET_OTHERHOST, 1, "to someone else"}, + {"outgoing", PACKET_OUTGOING, 1, "outgoing of any type"}, +*/ + /* aliases */ + {"bcast", PACKET_BROADCAST, 0, NULL}, + {"mcast", PACKET_MULTICAST, 0, NULL}, + {"host", PACKET_HOST, 0, NULL} +}; + +static void print_types(void) +{ + unsigned int i; + + printf("Valid packet types:\n"); + for (i = 0; i < ARRAY_SIZE(supported_types); ++i) + if(supported_types[i].printhelp == 1) + printf("\t%-14s\t\t%s\n", supported_types[i].name, supported_types[i].help); + printf("\n"); +} + +static void pkttype_help(void) +{ + printf( +"pkttype match options:\n" +"[!] --pkt-type packettype match packet type\n"); + print_types(); +} + +static const struct option pkttype_opts[] = { + {"pkt-type", 1, NULL, '1'}, + { .name = NULL } +}; + +static void parse_pkttype(const char *pkttype, struct xt_pkttype_info *info) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_types); ++i) + if(strcasecmp(pkttype, supported_types[i].name)==0) + { + info->pkttype=supported_types[i].pkttype; + return; + } + + xtables_error(PARAMETER_PROBLEM, "Bad packet type '%s'", pkttype); +} + +static int pkttype_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_pkttype_info *info = (struct xt_pkttype_info *)(*match)->data; + + switch(c) + { + case '1': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_pkttype(optarg, info); + if(invert) + info->invert=1; + *flags=1; + break; + + default: + return 0; + } + + return 1; +} + +static void pkttype_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, "You must specify \"--pkt-type\""); +} + +static void print_pkttype(const struct xt_pkttype_info *info) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_types); ++i) + if(supported_types[i].pkttype==info->pkttype) + { + printf("%s ", supported_types[i].name); + return; + } + + printf("%d ", info->pkttype); /* in case we didn't find an entry in named-packtes */ +} + +static void pkttype_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_pkttype_info *info = (const void *)match->data; + + printf("PKTTYPE %s= ", info->invert?"!":""); + print_pkttype(info); +} + +static void pkttype_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_pkttype_info *info = (const void *)match->data; + + printf("%s--pkt-type ", info->invert ? "! " : ""); + print_pkttype(info); +} + +static struct xtables_match pkttype_match = { + .family = NFPROTO_UNSPEC, + .name = "pkttype", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_pkttype_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_pkttype_info)), + .help = pkttype_help, + .parse = pkttype_parse, + .final_check = pkttype_check, + .print = pkttype_print, + .save = pkttype_save, + .extra_opts = pkttype_opts, +}; + +void _init(void) +{ + xtables_register_match(&pkttype_match); +} diff --git a/extensions/libxt_pkttype.man b/extensions/libxt_pkttype.man new file mode 100644 index 0000000..4560c76 --- /dev/null +++ b/extensions/libxt_pkttype.man @@ -0,0 +1,3 @@ +This module matches the link-layer packet type. +.TP +[\fB!\fP] \fB\-\-pkt\-type\fP {\fBunicast\fP|\fBbroadcast\fP|\fBmulticast\fP} diff --git a/extensions/libxt_policy.c b/extensions/libxt_policy.c new file mode 100644 index 0000000..a87ddd8 --- /dev/null +++ b/extensions/libxt_policy.c @@ -0,0 +1,513 @@ +/* Shared library add-on to iptables to add policy support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> +#include <getopt.h> +#include <netdb.h> +#include <errno.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <xtables.h> + +#include <linux/netfilter/xt_policy.h> + +/* + * HACK: global pointer to current matchinfo for making + * final checks and adjustments in final_check. + */ +static struct xt_policy_info *policy_info; + +static void policy_help(void) +{ + printf( +"policy match options:\n" +" --dir in|out match policy applied during decapsulation/\n" +" policy to be applied during encapsulation\n" +" --pol none|ipsec match policy\n" +" --strict match entire policy instead of single element\n" +" at any position\n" +"[!] --reqid reqid match reqid\n" +"[!] --spi spi match SPI\n" +"[!] --proto proto match protocol (ah/esp/ipcomp)\n" +"[!] --mode mode match mode (transport/tunnel)\n" +"[!] --tunnel-src addr/mask match tunnel source\n" +"[!] --tunnel-dst addr/mask match tunnel destination\n" +" --next begin next element in policy\n"); +} + +static const struct option policy_opts[] = +{ + { + .name = "dir", + .has_arg = 1, + .val = '1', + }, + { + .name = "pol", + .has_arg = 1, + .val = '2', + }, + { + .name = "strict", + .val = '3' + }, + { + .name = "reqid", + .has_arg = 1, + .val = '4', + }, + { + .name = "spi", + .has_arg = 1, + .val = '5' + }, + { + .name = "tunnel-src", + .has_arg = 1, + .val = '6' + }, + { + .name = "tunnel-dst", + .has_arg = 1, + .val = '7' + }, + { + .name = "proto", + .has_arg = 1, + .val = '8' + }, + { + .name = "mode", + .has_arg = 1, + .val = '9' + }, + { + .name = "next", + .val = 'a' + }, + { .name = NULL } +}; + +static int parse_direction(char *s) +{ + if (strcmp(s, "in") == 0) + return XT_POLICY_MATCH_IN; + if (strcmp(s, "out") == 0) + return XT_POLICY_MATCH_OUT; + xtables_error(PARAMETER_PROBLEM, "policy_match: invalid dir \"%s\"", s); +} + +static int parse_policy(char *s) +{ + if (strcmp(s, "none") == 0) + return XT_POLICY_MATCH_NONE; + if (strcmp(s, "ipsec") == 0) + return 0; + xtables_error(PARAMETER_PROBLEM, "policy match: invalid policy \"%s\"", s); +} + +static int parse_mode(char *s) +{ + if (strcmp(s, "transport") == 0) + return XT_POLICY_MODE_TRANSPORT; + if (strcmp(s, "tunnel") == 0) + return XT_POLICY_MODE_TUNNEL; + xtables_error(PARAMETER_PROBLEM, "policy match: invalid mode \"%s\"", s); +} + +static int policy_parse(int c, char **argv, int invert, unsigned int *flags, + struct xt_policy_info *info, uint8_t family) +{ + struct xt_policy_elem *e = &info->pol[info->len]; + struct in_addr *addr = NULL, mask; + struct in6_addr *addr6 = NULL, mask6; + unsigned int naddr = 0, num; + int mode; + + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + switch (c) { + case '1': + if (info->flags & (XT_POLICY_MATCH_IN | XT_POLICY_MATCH_OUT)) + xtables_error(PARAMETER_PROBLEM, + "policy match: double --dir option"); + if (invert) + xtables_error(PARAMETER_PROBLEM, + "policy match: can't invert --dir option"); + + info->flags |= parse_direction(optarg); + break; + case '2': + if (invert) + xtables_error(PARAMETER_PROBLEM, + "policy match: can't invert --policy option"); + + info->flags |= parse_policy(optarg); + break; + case '3': + if (info->flags & XT_POLICY_MATCH_STRICT) + xtables_error(PARAMETER_PROBLEM, + "policy match: double --strict option"); + + if (invert) + xtables_error(PARAMETER_PROBLEM, + "policy match: can't invert --strict option"); + + info->flags |= XT_POLICY_MATCH_STRICT; + break; + case '4': + if (e->match.reqid) + xtables_error(PARAMETER_PROBLEM, + "policy match: double --reqid option"); + + e->match.reqid = 1; + e->invert.reqid = invert; + if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "policy", "--spi", optarg); + e->reqid = num; + break; + case '5': + if (e->match.spi) + xtables_error(PARAMETER_PROBLEM, + "policy match: double --spi option"); + + e->match.spi = 1; + e->invert.spi = invert; + if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX)) + xtables_param_act(XTF_BAD_VALUE, "policy", "--spi", optarg); + e->spi = num; + break; + case '6': + if (e->match.saddr) + xtables_error(PARAMETER_PROBLEM, + "policy match: double --tunnel-src option"); + + if (family == NFPROTO_IPV6) + xtables_ip6parse_any(optarg, &addr6, &mask6, &naddr); + else + xtables_ipparse_any(optarg, &addr, &mask, &naddr); + if (naddr > 1) + xtables_error(PARAMETER_PROBLEM, + "policy match: name resolves to multiple IPs"); + + e->match.saddr = 1; + e->invert.saddr = invert; + if (family == NFPROTO_IPV6) { + memcpy(&e->saddr.a6, addr6, sizeof(*addr6)); + memcpy(&e->smask.a6, &mask6, sizeof(mask6)); + } else { + e->saddr.a4 = addr[0]; + e->smask.a4 = mask; + } + break; + case '7': + if (e->match.daddr) + xtables_error(PARAMETER_PROBLEM, + "policy match: double --tunnel-dst option"); + + if (family == NFPROTO_IPV6) + xtables_ip6parse_any(optarg, &addr6, &mask6, &naddr); + else + xtables_ipparse_any(optarg, &addr, &mask, &naddr); + if (naddr > 1) + xtables_error(PARAMETER_PROBLEM, + "policy match: name resolves to multiple IPs"); + + e->match.daddr = 1; + e->invert.daddr = invert; + if (family == NFPROTO_IPV6) { + memcpy(&e->daddr.a6, addr6, sizeof(*addr6)); + memcpy(&e->dmask.a6, &mask6, sizeof(mask6)); + } else { + e->daddr.a4 = addr[0]; + e->dmask.a4 = mask; + } + break; + case '8': + if (e->match.proto) + xtables_error(PARAMETER_PROBLEM, + "policy match: double --proto option"); + + e->proto = xtables_parse_protocol(optarg); + if (e->proto != IPPROTO_AH && e->proto != IPPROTO_ESP && + e->proto != IPPROTO_COMP) + xtables_error(PARAMETER_PROBLEM, + "policy match: protocol must ah/esp/ipcomp"); + e->match.proto = 1; + e->invert.proto = invert; + break; + case '9': + if (e->match.mode) + xtables_error(PARAMETER_PROBLEM, + "policy match: double --mode option"); + + mode = parse_mode(optarg); + e->match.mode = 1; + e->invert.mode = invert; + e->mode = mode; + break; + case 'a': + if (invert) + xtables_error(PARAMETER_PROBLEM, + "policy match: can't invert --next option"); + + if (++info->len == XT_POLICY_MAX_ELEM) + xtables_error(PARAMETER_PROBLEM, + "policy match: maximum policy depth reached"); + break; + default: + return 0; + } + + policy_info = info; + return 1; +} + +static int policy4_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + return policy_parse(c, argv, invert, flags, (void *)(*match)->data, + NFPROTO_IPV4); +} + +static int policy6_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + return policy_parse(c, argv, invert, flags, (void *)(*match)->data, + NFPROTO_IPV6); +} + +static void policy_check(unsigned int flags) +{ + struct xt_policy_info *info = policy_info; + struct xt_policy_elem *e; + int i; + + if (info == NULL) + xtables_error(PARAMETER_PROBLEM, + "policy match: no parameters given"); + + if (!(info->flags & (XT_POLICY_MATCH_IN | XT_POLICY_MATCH_OUT))) + xtables_error(PARAMETER_PROBLEM, + "policy match: neither --dir in nor --dir out specified"); + + if (info->flags & XT_POLICY_MATCH_NONE) { + if (info->flags & XT_POLICY_MATCH_STRICT) + xtables_error(PARAMETER_PROBLEM, + "policy match: policy none but --strict given"); + + if (info->len != 0) + xtables_error(PARAMETER_PROBLEM, + "policy match: policy none but policy given"); + } else + info->len++; /* increase len by 1, no --next after last element */ + + if (!(info->flags & XT_POLICY_MATCH_STRICT) && info->len > 1) + xtables_error(PARAMETER_PROBLEM, + "policy match: multiple elements but no --strict"); + + for (i = 0; i < info->len; i++) { + e = &info->pol[i]; + + if (info->flags & XT_POLICY_MATCH_STRICT && + !(e->match.reqid || e->match.spi || e->match.saddr || + e->match.daddr || e->match.proto || e->match.mode)) + xtables_error(PARAMETER_PROBLEM, + "policy match: empty policy element"); + + if ((e->match.saddr || e->match.daddr) + && ((e->mode == XT_POLICY_MODE_TUNNEL && e->invert.mode) || + (e->mode == XT_POLICY_MODE_TRANSPORT && !e->invert.mode))) + xtables_error(PARAMETER_PROBLEM, + "policy match: --tunnel-src/--tunnel-dst " + "is only valid in tunnel mode"); + } +} + +static void print_mode(const char *prefix, u_int8_t mode, int numeric) +{ + printf("%smode ", prefix); + + switch (mode) { + case XT_POLICY_MODE_TRANSPORT: + printf("transport "); + break; + case XT_POLICY_MODE_TUNNEL: + printf("tunnel "); + break; + default: + printf("??? "); + break; + } +} + +static void print_proto(const char *prefix, u_int8_t proto, int numeric) +{ + struct protoent *p = NULL; + + printf("%sproto ", prefix); + if (!numeric) + p = getprotobynumber(proto); + if (p != NULL) + printf("%s ", p->p_name); + else + printf("%u ", proto); +} + +#define PRINT_INVERT(x) \ +do { \ + if (x) \ + printf("! "); \ +} while(0) + +static void print_entry(const char *prefix, const struct xt_policy_elem *e, + bool numeric, uint8_t family) +{ + if (e->match.reqid) { + PRINT_INVERT(e->invert.reqid); + printf("%sreqid %u ", prefix, e->reqid); + } + if (e->match.spi) { + PRINT_INVERT(e->invert.spi); + printf("%sspi 0x%x ", prefix, e->spi); + } + if (e->match.proto) { + PRINT_INVERT(e->invert.proto); + print_proto(prefix, e->proto, numeric); + } + if (e->match.mode) { + PRINT_INVERT(e->invert.mode); + print_mode(prefix, e->mode, numeric); + } + if (e->match.daddr) { + PRINT_INVERT(e->invert.daddr); + if (family == NFPROTO_IPV6) + printf("%stunnel-dst %s%s ", prefix, + xtables_ip6addr_to_numeric(&e->daddr.a6), + xtables_ip6mask_to_numeric(&e->dmask.a6)); + else + printf("%stunnel-dst %s%s ", prefix, + xtables_ipaddr_to_numeric(&e->daddr.a4), + xtables_ipmask_to_numeric(&e->dmask.a4)); + } + if (e->match.saddr) { + PRINT_INVERT(e->invert.saddr); + if (family == NFPROTO_IPV6) + printf("%stunnel-src %s%s ", prefix, + xtables_ip6addr_to_numeric(&e->saddr.a6), + xtables_ip6mask_to_numeric(&e->smask.a6)); + else + printf("%stunnel-src %s%s ", prefix, + xtables_ipaddr_to_numeric(&e->saddr.a4), + xtables_ipmask_to_numeric(&e->smask.a4)); + } +} + +static void print_flags(char *prefix, const struct xt_policy_info *info) +{ + if (info->flags & XT_POLICY_MATCH_IN) + printf("%sdir in ", prefix); + else + printf("%sdir out ", prefix); + + if (info->flags & XT_POLICY_MATCH_NONE) + printf("%spol none ", prefix); + else + printf("%spol ipsec ", prefix); + + if (info->flags & XT_POLICY_MATCH_STRICT) + printf("%sstrict ", prefix); +} + +static void policy4_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_policy_info *info = (void *)match->data; + unsigned int i; + + printf("policy match "); + print_flags("", info); + for (i = 0; i < info->len; i++) { + if (info->len > 1) + printf("[%u] ", i); + print_entry("", &info->pol[i], numeric, NFPROTO_IPV4); + } +} + +static void policy6_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_policy_info *info = (void *)match->data; + unsigned int i; + + printf("policy match "); + print_flags("", info); + for (i = 0; i < info->len; i++) { + if (info->len > 1) + printf("[%u] ", i); + print_entry("", &info->pol[i], numeric, NFPROTO_IPV6); + } +} + +static void policy4_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_policy_info *info = (void *)match->data; + unsigned int i; + + print_flags("--", info); + for (i = 0; i < info->len; i++) { + print_entry("--", &info->pol[i], false, NFPROTO_IPV4); + if (i + 1 < info->len) + printf("--next "); + } +} + +static void policy6_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_policy_info *info = (void *)match->data; + unsigned int i; + + print_flags("--", info); + for (i = 0; i < info->len; i++) { + print_entry("--", &info->pol[i], false, NFPROTO_IPV6); + if (i + 1 < info->len) + printf("--next "); + } +} + +static struct xtables_match policy_mt_reg[] = { + { + .name = "policy", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct xt_policy_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_policy_info)), + .help = policy_help, + .parse = policy4_parse, + .final_check = policy_check, + .print = policy4_print, + .save = policy4_save, + .extra_opts = policy_opts, + }, + { + .name = "policy", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV6, + .size = XT_ALIGN(sizeof(struct xt_policy_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_policy_info)), + .help = policy_help, + .parse = policy6_parse, + .final_check = policy_check, + .print = policy6_print, + .save = policy6_save, + .extra_opts = policy_opts, + }, +}; + +void _init(void) +{ + xtables_register_matches(policy_mt_reg, ARRAY_SIZE(policy_mt_reg)); +} diff --git a/extensions/libxt_policy.man b/extensions/libxt_policy.man new file mode 100644 index 0000000..3500025 --- /dev/null +++ b/extensions/libxt_policy.man @@ -0,0 +1,48 @@ +This modules matches the policy used by IPsec for handling a packet. +.TP +\fB\-\-dir\fP {\fBin\fP|\fBout\fP} +Used to select whether to match the policy used for decapsulation or the +policy that will be used for encapsulation. +.B in +is valid in the +.B PREROUTING, INPUT and FORWARD +chains, +.B out +is valid in the +.B POSTROUTING, OUTPUT and FORWARD +chains. +.TP +\fB\-\-pol\fP {\fBnone\fP|\fBipsec\fP} +Matches if the packet is subject to IPsec processing. +.TP +\fB\-\-strict\fP +Selects whether to match the exact policy or match if any rule of +the policy matches the given policy. +.TP +[\fB!\fP] \fB\-\-reqid\fP \fIid\fP +Matches the reqid of the policy rule. The reqid can be specified with +.B setkey(8) +using +.B unique:id +as level. +.TP +[\fB!\fP] \fB\-\-spi\fP \fIspi\fP +Matches the SPI of the SA. +.TP +[\fB!\fP] \fB\-\-proto\fP {\fBah\fP|\fBesp\fP|\fBipcomp\fP} +Matches the encapsulation protocol. +.TP +[\fB!\fP] \fB\-\-mode\fP {\fBtunnel\fP|\fBtransport\fP} +Matches the encapsulation mode. +.TP +[\fB!\fP] \fB\-\-tunnel\-src\fP \fIaddr\fP[\fB/\fP\fImask\fP] +Matches the source end-point address of a tunnel mode SA. +Only valid with \fB\-\-mode tunnel\fP. +.TP +[\fB!\fP] \fB\-\-tunnel\-dst\fP \fIaddr\fP[\fB/\fP\fImask\fP] +Matches the destination end-point address of a tunnel mode SA. +Only valid with \fB\-\-mode tunnel\fP. +.TP +\fB\-\-next\fP +Start the next element in the policy specification. Can only be used with +\fB\-\-strict\fP. diff --git a/extensions/libxt_quota.c b/extensions/libxt_quota.c new file mode 100644 index 0000000..ac7c686 --- /dev/null +++ b/extensions/libxt_quota.c @@ -0,0 +1,97 @@ +/* + * Shared library add-on to iptables to add quota support + * + * Sam Johnston <samj@samj.net> + */ +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> + +#include <linux/netfilter/xt_quota.h> + +static const struct option quota_opts[] = { + {"quota", 1, NULL, '1'}, + { .name = NULL } +}; + +static void quota_help(void) +{ + printf("quota match options:\n" + "[!] --quota quota quota (bytes)\n"); +} + +static void +quota_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_quota_info *q = (const void *)match->data; + printf("quota: %llu bytes", (unsigned long long) q->quota); +} + +static void +quota_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_quota_info *q = (const void *)match->data; + printf("--quota %llu ", (unsigned long long) q->quota); +} + +/* parse quota option */ +static int +parse_quota(const char *s, u_int64_t * quota) +{ + *quota = strtoull(s, NULL, 10); + +#ifdef DEBUG_XT_QUOTA + printf("Quota: %llu\n", *quota); +#endif + + if (*quota == UINT64_MAX) + xtables_error(PARAMETER_PROBLEM, "quota invalid: '%s'\n", s); + else + return 1; +} + +static int +quota_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_quota_info *info = (struct xt_quota_info *) (*match)->data; + + switch (c) { + case '1': + if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) + xtables_error(PARAMETER_PROBLEM, "quota: unexpected '!'"); + if (!parse_quota(optarg, &info->quota)) + xtables_error(PARAMETER_PROBLEM, + "bad quota: '%s'", optarg); + + if (invert) + info->flags |= XT_QUOTA_INVERT; + + break; + + default: + return 0; + } + return 1; +} + +static struct xtables_match quota_match = { + .family = NFPROTO_UNSPEC, + .name = "quota", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof (struct xt_quota_info)), + .userspacesize = offsetof(struct xt_quota_info, quota), + .help = quota_help, + .parse = quota_parse, + .print = quota_print, + .save = quota_save, + .extra_opts = quota_opts, +}; + +void +_init(void) +{ + xtables_register_match("a_match); +} diff --git a/extensions/libxt_quota.man b/extensions/libxt_quota.man new file mode 100644 index 0000000..8d9e18b --- /dev/null +++ b/extensions/libxt_quota.man @@ -0,0 +1,5 @@ +Implements network quotas by decrementing a byte counter with each +packet. +.TP +[\fB!\fP] \fB\-\-quota\fP \fIbytes\fP +The quota in bytes. diff --git a/extensions/libxt_rateest.c b/extensions/libxt_rateest.c new file mode 100644 index 0000000..ad0884e --- /dev/null +++ b/extensions/libxt_rateest.c @@ -0,0 +1,453 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/xt_rateest.h> + +/* Ugly hack to pass info to final_check function. We should fix the API */ +static struct xt_rateest_match_info *rateest_info; + +static void rateest_help(void) +{ + printf( +"rateest match options:\n" +" --rateest1 name Rate estimator name\n" +" --rateest2 name Rate estimator name\n" +" --rateest-delta Compare difference(s) to given rate(s)\n" +" --rateest-bps1 [bps] Compare bps\n" +" --rateest-pps1 [pps] Compare pps\n" +" --rateest-bps2 [bps] Compare bps\n" +" --rateest-pps2 [pps] Compare pps\n" +" [!] --rateest-lt Match if rate is less than given rate/estimator\n" +" [!] --rateest-gt Match if rate is greater than given rate/estimator\n" +" [!] --rateest-eq Match if rate is equal to given rate/estimator\n"); +} + +enum rateest_options { + OPT_RATEEST1, + OPT_RATEEST2, + OPT_RATEEST_BPS1, + OPT_RATEEST_PPS1, + OPT_RATEEST_BPS2, + OPT_RATEEST_PPS2, + OPT_RATEEST_DELTA, + OPT_RATEEST_LT, + OPT_RATEEST_GT, + OPT_RATEEST_EQ, +}; + +static const struct option rateest_opts[] = { + { "rateest1", 1, NULL, OPT_RATEEST1 }, + { "rateest", 1, NULL, OPT_RATEEST1 }, /* alias for absolute mode */ + { "rateest2", 1, NULL, OPT_RATEEST2 }, + { "rateest-bps1", 0, NULL, OPT_RATEEST_BPS1 }, + { "rateest-pps1", 0, NULL, OPT_RATEEST_PPS1 }, + { "rateest-bps2", 0, NULL, OPT_RATEEST_BPS2 }, + { "rateest-pps2", 0, NULL, OPT_RATEEST_PPS2 }, + { "rateest-bps", 0, NULL, OPT_RATEEST_BPS2 }, /* alias for absolute mode */ + { "rateest-pps", 0, NULL, OPT_RATEEST_PPS2 }, /* alias for absolute mode */ + { "rateest-delta", 0, NULL, OPT_RATEEST_DELTA }, + { "rateest-lt", 0, NULL, OPT_RATEEST_LT }, + { "rateest-gt", 0, NULL, OPT_RATEEST_GT }, + { "rateest-eq", 0, NULL, OPT_RATEEST_EQ }, + { .name = NULL } +}; + +/* Copied from iproute. See http://physics.nist.gov/cuu/Units/binary.html */ +static const struct rate_suffix { + const char *name; + double scale; +} suffixes[] = { + { "bit", 1. }, + { "Kibit", 1024. }, + { "kbit", 1000. }, + { "mibit", 1024.*1024. }, + { "mbit", 1000000. }, + { "gibit", 1024.*1024.*1024. }, + { "gbit", 1000000000. }, + { "tibit", 1024.*1024.*1024.*1024. }, + { "tbit", 1000000000000. }, + { "Bps", 8. }, + { "KiBps", 8.*1024. }, + { "KBps", 8000. }, + { "MiBps", 8.*1024*1024. }, + { "MBps", 8000000. }, + { "GiBps", 8.*1024.*1024.*1024. }, + { "GBps", 8000000000. }, + { "TiBps", 8.*1024.*1024.*1024.*1024. }, + { "TBps", 8000000000000. }, + { .name = NULL } +}; + +static int +rateest_get_rate(u_int32_t *rate, const char *str) +{ + char *p; + double bps = strtod(str, &p); + const struct rate_suffix *s; + + if (p == str) + return -1; + + if (*p == '\0') { + *rate = bps / 8.; /* assume bytes/sec */ + return 0; + } + + for (s = suffixes; s->name; ++s) { + if (strcasecmp(s->name, p) == 0) { + *rate = (bps * s->scale) / 8.; + return 0; + } + } + + return -1; +} + +static int +rateest_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_rateest_match_info *info = (void *)(*match)->data; + unsigned int val; + + rateest_info = info; + + switch (c) { + case OPT_RATEEST1: + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) + xtables_error(PARAMETER_PROBLEM, + "rateest: rateest can't be inverted"); + + if (*flags & (1 << c)) + xtables_error(PARAMETER_PROBLEM, + "rateest: can't specify --rateest1 twice"); + *flags |= 1 << c; + + strncpy(info->name1, optarg, sizeof(info->name1) - 1); + break; + + case OPT_RATEEST2: + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) + xtables_error(PARAMETER_PROBLEM, + "rateest: rateest can't be inverted"); + + if (*flags & (1 << c)) + xtables_error(PARAMETER_PROBLEM, + "rateest: can't specify --rateest2 twice"); + *flags |= 1 << c; + + strncpy(info->name2, optarg, sizeof(info->name2) - 1); + info->flags |= XT_RATEEST_MATCH_REL; + break; + + case OPT_RATEEST_BPS1: + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) + xtables_error(PARAMETER_PROBLEM, + "rateest: rateest-bps can't be inverted"); + + if (*flags & (1 << c)) + xtables_error(PARAMETER_PROBLEM, + "rateest: can't specify --rateest-bps1 twice"); + *flags |= 1 << c; + + info->flags |= XT_RATEEST_MATCH_BPS; + + /* The rate is optional and only required in absolute mode */ + if (!argv[optind] || *argv[optind] == '-' || *argv[optind] == '!') + break; + + if (rateest_get_rate(&info->bps1, argv[optind]) < 0) + xtables_error(PARAMETER_PROBLEM, + "rateest: could not parse rate `%s'", + argv[optind]); + optind++; + break; + + case OPT_RATEEST_PPS1: + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) + xtables_error(PARAMETER_PROBLEM, + "rateest: rateest-pps can't be inverted"); + + if (*flags & (1 << c)) + xtables_error(PARAMETER_PROBLEM, + "rateest: can't specify --rateest-pps1 twice"); + *flags |= 1 << c; + + info->flags |= XT_RATEEST_MATCH_PPS; + + /* The rate is optional and only required in absolute mode */ + if (!argv[optind] || *argv[optind] == '-' || *argv[optind] == '!') + break; + + if (!xtables_strtoui(argv[optind], NULL, &val, 0, UINT32_MAX)) + xtables_error(PARAMETER_PROBLEM, + "rateest: could not parse pps `%s'", + argv[optind]); + info->pps1 = val; + optind++; + break; + + case OPT_RATEEST_BPS2: + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) + xtables_error(PARAMETER_PROBLEM, + "rateest: rateest-bps can't be inverted"); + + if (*flags & (1 << c)) + xtables_error(PARAMETER_PROBLEM, + "rateest: can't specify --rateest-bps2 twice"); + *flags |= 1 << c; + + info->flags |= XT_RATEEST_MATCH_BPS; + + /* The rate is optional and only required in absolute mode */ + if (!argv[optind] || *argv[optind] == '-' || *argv[optind] == '!') + break; + + if (rateest_get_rate(&info->bps2, argv[optind]) < 0) + xtables_error(PARAMETER_PROBLEM, + "rateest: could not parse rate `%s'", + argv[optind]); + optind++; + break; + + case OPT_RATEEST_PPS2: + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) + xtables_error(PARAMETER_PROBLEM, + "rateest: rateest-pps can't be inverted"); + + if (*flags & (1 << c)) + xtables_error(PARAMETER_PROBLEM, + "rateest: can't specify --rateest-pps2 twice"); + *flags |= 1 << c; + + info->flags |= XT_RATEEST_MATCH_PPS; + + /* The rate is optional and only required in absolute mode */ + if (!argv[optind] || *argv[optind] == '-' || *argv[optind] == '!') + break; + + if (!xtables_strtoui(argv[optind], NULL, &val, 0, UINT32_MAX)) + xtables_error(PARAMETER_PROBLEM, + "rateest: could not parse pps `%s'", + argv[optind]); + info->pps2 = val; + optind++; + break; + + case OPT_RATEEST_DELTA: + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) + xtables_error(PARAMETER_PROBLEM, + "rateest: rateest-delta can't be inverted"); + + if (*flags & (1 << c)) + xtables_error(PARAMETER_PROBLEM, + "rateest: can't specify --rateest-delta twice"); + *flags |= 1 << c; + + info->flags |= XT_RATEEST_MATCH_DELTA; + break; + + case OPT_RATEEST_EQ: + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + if (*flags & (1 << c)) + xtables_error(PARAMETER_PROBLEM, + "rateest: can't specify lt/gt/eq twice"); + *flags |= 1 << c; + + info->mode = XT_RATEEST_MATCH_EQ; + if (invert) + info->flags |= XT_RATEEST_MATCH_INVERT; + break; + + case OPT_RATEEST_LT: + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + if (*flags & (1 << c)) + xtables_error(PARAMETER_PROBLEM, + "rateest: can't specify lt/gt/eq twice"); + *flags |= 1 << c; + + info->mode = XT_RATEEST_MATCH_LT; + if (invert) + info->flags |= XT_RATEEST_MATCH_INVERT; + break; + + case OPT_RATEEST_GT: + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + if (*flags & (1 << c)) + xtables_error(PARAMETER_PROBLEM, + "rateest: can't specify lt/gt/eq twice"); + *flags |= 1 << c; + + info->mode = XT_RATEEST_MATCH_GT; + if (invert) + info->flags |= XT_RATEEST_MATCH_INVERT; + break; + + default: + return 0; + } + + return 1; +} + +static void +rateest_final_check(unsigned int flags) +{ + struct xt_rateest_match_info *info = rateest_info; + + if (info == NULL) + xtables_error(PARAMETER_PROBLEM, "rateest match: " + "you need to specify some flags"); + if (!(info->flags & XT_RATEEST_MATCH_REL)) + info->flags |= XT_RATEEST_MATCH_ABS; +} + +static void +rateest_print_rate(u_int32_t rate, int numeric) +{ + double tmp = (double)rate*8; + + if (numeric) + printf("%u ", rate); + else if (tmp >= 1000.0*1000000.0) + printf("%.0fMbit ", tmp/1000000.0); + else if (tmp >= 1000.0 * 1000.0) + printf("%.0fKbit ", tmp/1000.0); + else + printf("%.0fbit ", tmp); +} + +static void +rateest_print_mode(const struct xt_rateest_match_info *info, + const char *prefix) +{ + if (info->flags & XT_RATEEST_MATCH_INVERT) + printf("! "); + + switch (info->mode) { + case XT_RATEEST_MATCH_EQ: + printf("%seq ", prefix); + break; + case XT_RATEEST_MATCH_LT: + printf("%slt ", prefix); + break; + case XT_RATEEST_MATCH_GT: + printf("%sgt ", prefix); + break; + default: + exit(1); + } +} + +static void +rateest_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_rateest_match_info *info = (const void *)match->data; + + printf("rateest match "); + + printf("%s ", info->name1); + if (info->flags & XT_RATEEST_MATCH_DELTA) + printf("delta "); + + if (info->flags & XT_RATEEST_MATCH_BPS) { + printf("bps "); + if (info->flags & XT_RATEEST_MATCH_DELTA) + rateest_print_rate(info->bps1, numeric); + if (info->flags & XT_RATEEST_MATCH_ABS) { + rateest_print_mode(info, ""); + rateest_print_rate(info->bps2, numeric); + } + } + if (info->flags & XT_RATEEST_MATCH_PPS) { + printf("pps "); + if (info->flags & XT_RATEEST_MATCH_DELTA) + printf("%u ", info->pps1); + if (info->flags & XT_RATEEST_MATCH_ABS) { + rateest_print_mode(info, ""); + printf("%u ", info->pps2); + } + } + + if (info->flags & XT_RATEEST_MATCH_REL) { + rateest_print_mode(info, ""); + + printf("%s ", info->name2); + if (info->flags & XT_RATEEST_MATCH_DELTA) + printf("delta "); + + if (info->flags & XT_RATEEST_MATCH_BPS) { + printf("bps "); + if (info->flags & XT_RATEEST_MATCH_DELTA) + rateest_print_rate(info->bps2, numeric); + } + if (info->flags & XT_RATEEST_MATCH_PPS) { + printf("pps "); + if (info->flags & XT_RATEEST_MATCH_DELTA) + printf("%u ", info->pps2); + } + } +} + +static void +rateest_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_rateest_match_info *info = (const void *)match->data; + + if (info->flags & XT_RATEEST_MATCH_REL) { + printf("--rateest1 %s ", info->name1); + if (info->flags & XT_RATEEST_MATCH_BPS) + printf("--rateest-bps "); + if (info->flags & XT_RATEEST_MATCH_PPS) + printf("--rateest-pps "); + rateest_print_mode(info, "--rateest-"); + printf("--rateest2 %s ", info->name2); + } else { + printf("--rateest %s ", info->name1); + if (info->flags & XT_RATEEST_MATCH_BPS) { + printf("--rateest-bps1 "); + rateest_print_rate(info->bps1, 0); + printf("--rateest-bps2 "); + rateest_print_rate(info->bps2, 0); + rateest_print_mode(info, "--rateest-"); + } + if (info->flags & XT_RATEEST_MATCH_PPS) { + printf("--rateest-pps "); + rateest_print_mode(info, "--rateest-"); + printf("%u ", info->pps2); + } + } +} + +static struct xtables_match rateest_mt_reg = { + .family = NFPROTO_UNSPEC, + .name = "rateest", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_rateest_match_info)), + .userspacesize = XT_ALIGN(offsetof(struct xt_rateest_match_info, est1)), + .help = rateest_help, + .parse = rateest_parse, + .final_check = rateest_final_check, + .print = rateest_print, + .save = rateest_save, + .extra_opts = rateest_opts, +}; + +void _init(void) +{ + xtables_register_match(&rateest_mt_reg); +} diff --git a/extensions/libxt_rateest.man b/extensions/libxt_rateest.man new file mode 100644 index 0000000..de064af --- /dev/null +++ b/extensions/libxt_rateest.man @@ -0,0 +1,55 @@ +The rate estimator can match on estimated rates as collected by the RATEEST +target. It supports matching on absolute bps/pps values, comparing two rate +estimators and matching on the difference between two rate estimators. +.TP +\fB\-\-rateest1\fP \fIname\fP +Name of the first rate estimator. +.TP +\fB\-\-rateest2\fP \fIname\fP +Name of the second rate estimator (if difference is to be calculated). +.TP +\fB\-\-rateest\-delta\fP +Compare difference(s) to given rate(s) +.TP +\fB\-\-rateest\-bps1\fP \fIvalue\fP +.TP +\fB\-\-rateest\-bps2\fP \fIvalue\fP +Compare bytes per second. +.TP +\fB\-\-rateest\-pps1\fP \fIvalue\fP +.TP +\fB\-\-rateest\-pps2\fP \fIvalue\fP +Compare packets per second. +.TP +[\fB!\fP] \fB\-\-rateest\-lt\fP +Match if rate is less than given rate/estimator. +.TP +[\fB!\fP] \fB\-\-rateest\-gt\fP +Match if rate is greater than given rate/estimator. +.TP +[\fB!\fP] \fB\-\-rateest\-eq\fP +Match if rate is equal to given rate/estimator. +.PP +Example: This is what can be used to route outgoing data connections from an +FTP server over two lines based on the available bandwidth at the time the data +connection was started: +.PP +# Estimate outgoing rates +.PP +iptables \-t mangle \-A POSTROUTING \-o eth0 \-j RATEEST \-\-rateest\-name eth0 +\-\-rateest\-interval 250ms \-\-rateest\-ewma 0.5s +.PP +iptables \-t mangle \-A POSTROUTING \-o ppp0 \-j RATEEST \-\-rateest\-name ppp0 +\-\-rateest\-interval 250ms \-\-rateest\-ewma 0.5s +.PP +# Mark based on available bandwidth +.PP +iptables \-t mangle \-A balance \-m conntrack \-\-ctstate NEW \-m helper \-\-helper ftp +\-m rateest \-\-rateest\-delta \-\-rateest1 eth0 \-\-rateest\-bps1 2.5mbit \-\-rateest\-gt +\-\-rateest2 ppp0 \-\-rateest\-bps2 2mbit \-j CONNMARK \-\-set\-mark 1 +.PP +iptables \-t mangle \-A balance \-m conntrack \-\-ctstate NEW \-m helper \-\-helper ftp +\-m rateest \-\-rateest\-delta \-\-rateest1 ppp0 \-\-rateest\-bps1 2mbit \-\-rateest\-gt +\-\-rateest2 eth0 \-\-rateest\-bps2 2.5mbit \-j CONNMARK \-\-set\-mark 2 +.PP +iptables \-t mangle \-A balance \-j CONNMARK \-\-restore\-mark diff --git a/extensions/libxt_recent.c b/extensions/libxt_recent.c new file mode 100644 index 0000000..ecc17ad --- /dev/null +++ b/extensions/libxt_recent.c @@ -0,0 +1,233 @@ +/* Shared library add-on to iptables to add recent matching support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/xt_recent.h> + +static const struct option recent_opts[] = { + { .name = "set", .has_arg = 0, .val = 201 }, + { .name = "rcheck", .has_arg = 0, .val = 202 }, + { .name = "update", .has_arg = 0, .val = 203 }, + { .name = "seconds", .has_arg = 1, .val = 204 }, + { .name = "hitcount", .has_arg = 1, .val = 205 }, + { .name = "remove", .has_arg = 0, .val = 206 }, + { .name = "rttl", .has_arg = 0, .val = 207 }, + { .name = "name", .has_arg = 1, .val = 208 }, + { .name = "rsource", .has_arg = 0, .val = 209 }, + { .name = "rdest", .has_arg = 0, .val = 210 }, + { .name = NULL } +}; + +static void recent_help(void) +{ + printf( +"recent match options:\n" +"[!] --set Add source address to list, always matches.\n" +"[!] --rcheck Match if source address in list.\n" +"[!] --update Match if source address in list, also update last-seen time.\n" +"[!] --remove Match if source address in list, also removes that address from list.\n" +" --seconds seconds For check and update commands above.\n" +" Specifies that the match will only occur if source address last seen within\n" +" the last 'seconds' seconds.\n" +" --hitcount hits For check and update commands above.\n" +" Specifies that the match will only occur if source address seen hits times.\n" +" May be used in conjunction with the seconds option.\n" +" --rttl For check and update commands above.\n" +" Specifies that the match will only occur if the source address and the TTL\n" +" match between this packet and the one which was set.\n" +" Useful if you have problems with people spoofing their source address in order\n" +" to DoS you via this module.\n" +" --name name Name of the recent list to be used. DEFAULT used if none given.\n" +" --rsource Match/Save the source address of each packet in the recent list table (default).\n" +" --rdest Match/Save the destination address of each packet in the recent list table.\n" +"xt_recent by: Stephen Frost <sfrost@snowman.net>. http://snowman.net/projects/ipt_recent/\n"); +} + +static void recent_init(struct xt_entry_match *match) +{ + struct xt_recent_mtinfo *info = (void *)(match)->data; + + strncpy(info->name,"DEFAULT", XT_RECENT_NAME_LEN); + /* even though XT_RECENT_NAME_LEN is currently defined as 200, + * better be safe, than sorry */ + info->name[XT_RECENT_NAME_LEN-1] = '\0'; + info->side = XT_RECENT_SOURCE; +} + +#define RECENT_CMDS \ + (XT_RECENT_SET | XT_RECENT_CHECK | \ + XT_RECENT_UPDATE | XT_RECENT_REMOVE) + +static int recent_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_recent_mtinfo *info = (void *)(*match)->data; + + switch (c) { + case 201: + if (*flags & RECENT_CMDS) + xtables_error(PARAMETER_PROBLEM, + "recent: only one of `--set', `--rcheck' " + "`--update' or `--remove' may be set"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + info->check_set |= XT_RECENT_SET; + if (invert) info->invert = 1; + *flags |= XT_RECENT_SET; + break; + + case 202: + if (*flags & RECENT_CMDS) + xtables_error(PARAMETER_PROBLEM, + "recent: only one of `--set', `--rcheck' " + "`--update' or `--remove' may be set"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + info->check_set |= XT_RECENT_CHECK; + if(invert) info->invert = 1; + *flags |= XT_RECENT_CHECK; + break; + + case 203: + if (*flags & RECENT_CMDS) + xtables_error(PARAMETER_PROBLEM, + "recent: only one of `--set', `--rcheck' " + "`--update' or `--remove' may be set"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + info->check_set |= XT_RECENT_UPDATE; + if (invert) info->invert = 1; + *flags |= XT_RECENT_UPDATE; + break; + + case 204: + info->seconds = atoi(optarg); + break; + + case 205: + info->hit_count = atoi(optarg); + break; + + case 206: + if (*flags & RECENT_CMDS) + xtables_error(PARAMETER_PROBLEM, + "recent: only one of `--set', `--rcheck' " + "`--update' or `--remove' may be set"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + info->check_set |= XT_RECENT_REMOVE; + if (invert) info->invert = 1; + *flags |= XT_RECENT_REMOVE; + break; + + case 207: + info->check_set |= XT_RECENT_TTL; + *flags |= XT_RECENT_TTL; + break; + + case 208: + strncpy(info->name,optarg, XT_RECENT_NAME_LEN); + info->name[XT_RECENT_NAME_LEN-1] = '\0'; + break; + + case 209: + info->side = XT_RECENT_SOURCE; + break; + + case 210: + info->side = XT_RECENT_DEST; + break; + + default: + return 0; + } + + return 1; +} + +static void recent_check(unsigned int flags) +{ + if (!(flags & RECENT_CMDS)) + xtables_error(PARAMETER_PROBLEM, + "recent: you must specify one of `--set', `--rcheck' " + "`--update' or `--remove'"); + if ((flags & XT_RECENT_TTL) && + (flags & (XT_RECENT_SET | XT_RECENT_REMOVE))) + xtables_error(PARAMETER_PROBLEM, + "recent: --rttl may only be used with --rcheck or " + "--update"); +} + +static void recent_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_recent_mtinfo *info = (const void *)match->data; + + if (info->invert) + fputc('!', stdout); + + printf("recent: "); + if (info->check_set & XT_RECENT_SET) + printf("SET "); + if (info->check_set & XT_RECENT_CHECK) + printf("CHECK "); + if (info->check_set & XT_RECENT_UPDATE) + printf("UPDATE "); + if (info->check_set & XT_RECENT_REMOVE) + printf("REMOVE "); + if(info->seconds) printf("seconds: %d ",info->seconds); + if(info->hit_count) printf("hit_count: %d ",info->hit_count); + if (info->check_set & XT_RECENT_TTL) + printf("TTL-Match "); + if(info->name) printf("name: %s ",info->name); + if (info->side == XT_RECENT_SOURCE) + printf("side: source "); + if (info->side == XT_RECENT_DEST) + printf("side: dest "); +} + +static void recent_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_recent_mtinfo *info = (const void *)match->data; + + if (info->invert) + printf("! "); + + if (info->check_set & XT_RECENT_SET) + printf("--set "); + if (info->check_set & XT_RECENT_CHECK) + printf("--rcheck "); + if (info->check_set & XT_RECENT_UPDATE) + printf("--update "); + if (info->check_set & XT_RECENT_REMOVE) + printf("--remove "); + if(info->seconds) printf("--seconds %d ",info->seconds); + if(info->hit_count) printf("--hitcount %d ",info->hit_count); + if (info->check_set & XT_RECENT_TTL) + printf("--rttl "); + if(info->name) printf("--name %s ",info->name); + if (info->side == XT_RECENT_SOURCE) + printf("--rsource "); + if (info->side == XT_RECENT_DEST) + printf("--rdest "); +} + +static struct xtables_match recent_mt_reg = { + .name = "recent", + .version = XTABLES_VERSION, + .family = NFPROTO_UNSPEC, + .size = XT_ALIGN(sizeof(struct xt_recent_mtinfo)), + .userspacesize = XT_ALIGN(sizeof(struct xt_recent_mtinfo)), + .help = recent_help, + .init = recent_init, + .parse = recent_parse, + .final_check = recent_check, + .print = recent_print, + .save = recent_save, + .extra_opts = recent_opts, +}; + +void _init(void) +{ + xtables_register_match(&recent_mt_reg); +} diff --git a/extensions/libxt_recent.man b/extensions/libxt_recent.man new file mode 100644 index 0000000..532c328 --- /dev/null +++ b/extensions/libxt_recent.man @@ -0,0 +1,104 @@ +Allows you to dynamically create a list of IP addresses and then match against +that list in a few different ways. +.PP +For example, you can create a "badguy" list out of people attempting to connect +to port 139 on your firewall and then DROP all future packets from them without +considering them. +.PP +\fB\-\-set\fP, \fB\-\-rcheck\fP, \fB\-\-update\fP and \fB\-\-remove\fP are +mutually exclusive. +.TP +\fB\-\-name\fP \fIname\fP +Specify the list to use for the commands. If no name is given then +\fBDEFAULT\fR will be used. +.TP +[\fB!\fR] \fB\-\-set\fP +This will add the source address of the packet to the list. If the source +address is already in the list, this will update the existing entry. This will +always return success (or failure if \fB!\fR is passed in). +.TP +\fB\-\-rsource\fP +Match/save the source address of each packet in the recent list table. This +is the default. +.TP +\fB\-\-rdest\fP +Match/save the destination address of each packet in the recent list table. +.TP +[\fB!\fR] \fB\-\-rcheck\fP +Check if the source address of the packet is currently in the list. +.TP +[\fB!\fR] \fB\-\-update\fP +Like \fB\-\-rcheck\fP, except it will update the "last seen" timestamp if it +matches. +.TP +[\fB!\fR] \fB\-\-remove\fP +Check if the source address of the packet is currently in the list and if so +that address will be removed from the list and the rule will return true. If +the address is not found, false is returned. +.TP +\fB\-\-seconds\fP \fIseconds\fP +This option must be used in conjunction with one of \fB\-\-rcheck\fP or +\fB\-\-update\fP. When used, this will narrow the match to only happen when the +address is in the list and was seen within the last given number of seconds. +.TP +\fB\-\-hitcount\fP \fIhits\fP +This option must be used in conjunction with one of \fB\-\-rcheck\fP or +\fB\-\-update\fP. When used, this will narrow the match to only happen when the +address is in the list and packets had been received greater than or equal to +the given value. This option may be used along with \fB\-\-seconds\fP to create +an even narrower match requiring a certain number of hits within a specific +time frame. The maximum value for the hitcount parameter is given by the +"ip_pkt_list_tot" parameter of the xt_recent kernel module. Exceeding this +value on the command line will cause the rule to be rejected. +.TP +\fB\-\-rttl\fP +This option may only be used in conjunction with one of \fB\-\-rcheck\fP or +\fB\-\-update\fP. When used, this will narrow the match to only happen when the +address is in the list and the TTL of the current packet matches that of the +packet which hit the \fB\-\-set\fP rule. This may be useful if you have problems +with people faking their source address in order to DoS you via this module by +disallowing others access to your site by sending bogus packets to you. +.PP +Examples: +.IP +iptables \-A FORWARD \-m recent \-\-name badguy \-\-rcheck \-\-seconds 60 \-j DROP +.IP +iptables \-A FORWARD \-p tcp \-i eth0 \-\-dport 139 \-m recent \-\-name badguy \-\-set \-j DROP +.PP +Steve's ipt_recent website (http://snowman.net/projects/ipt_recent/) also has +some examples of usage. +.PP +\fB/proc/net/xt_recent/*\fR are the current lists of addresses and information +about each entry of each list. +.PP +Each file in \fB/proc/net/xt_recent/\fR can be read from to see the current +list or written two using the following commands to modify the list: +.TP +\fBecho +\fR\fIaddr\fR\fB >/proc/net/xt_recent/DEFAULT\fR +to add \fIaddr\fR to the DEFAULT list +.TP +\fBecho \-\fP\fIaddr\fP\fB >/proc/net/xt_recent/DEFAULT\fP +to remove \fIaddr\fR from the DEFAULT list +.TP +\fBecho / >/proc/net/xt_recent/DEFAULT\fR +to flush the DEFAULT list (remove all entries). +.PP +The module itself accepts parameters, defaults shown: +.TP +\fBip_list_tot\fR=\fI100\fR +Number of addresses remembered per table. +.TP +\fBip_pkt_list_tot\fR=\fI20\fR +Number of packets per address remembered. +.TP +\fBip_list_hash_size\fR=\fI0\fR +Hash table size. 0 means to calculate it based on ip_list_tot, default: 512. +.TP +\fBip_list_perms\fR=\fI0644\fR +Permissions for /proc/net/xt_recent/* files. +.TP +\fBip_list_uid\fR=\fI0\fR +Numerical UID for ownership of /proc/net/xt_recent/* files. +.TP +\fBip_list_gid\fR=\fI0\fR +Numerical GID for ownership of /proc/net/xt_recent/* files. diff --git a/extensions/libxt_sctp.c b/extensions/libxt_sctp.c new file mode 100644 index 0000000..d321fb8 --- /dev/null +++ b/extensions/libxt_sctp.c @@ -0,0 +1,515 @@ +/* Shared library add-on to iptables for SCTP matching + * + * (C) 2003 by Harald Welte <laforge@gnumonks.org> + * + * This program is distributed under the terms of GNU GPL v2, 1991 + * + * libipt_ecn.c borrowed heavily from libipt_dscp.c + * + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <netdb.h> +#include <ctype.h> + +#include <netinet/in.h> +#include <xtables.h> + +#include <linux/netfilter/xt_sctp.h> + +#if 0 +#define DEBUGP(format, first...) printf(format, ##first) +#define static +#else +#define DEBUGP(format, fist...) +#endif + +static void +print_chunk(u_int32_t chunknum, int numeric); + +static void sctp_init(struct xt_entry_match *m) +{ + int i; + struct xt_sctp_info *einfo = (struct xt_sctp_info *)m->data; + + memset(einfo, 0, sizeof(struct xt_sctp_info)); + + for (i = 0; i < XT_NUM_SCTP_FLAGS; i++) { + einfo->flag_info[i].chunktype = -1; + } +} + +static void sctp_help(void) +{ + printf( +"sctp match options\n" +"[!] --source-port port[:port] match source port(s)\n" +" --sport ...\n" +"[!] --destination-port port[:port] match destination port(s)\n" +" --dport ...\n" +"[!] --chunk-types (all|any|none) (chunktype[:flags])+ match if all, any or none of\n" +" chunktypes are present\n" +"chunktypes - DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK FORWARD_TSN ALL NONE\n"); +} + +static const struct option sctp_opts[] = { + { .name = "source-port", .has_arg = 1, .val = '1' }, + { .name = "sport", .has_arg = 1, .val = '1' }, + { .name = "destination-port", .has_arg = 1, .val = '2' }, + { .name = "dport", .has_arg = 1, .val = '2' }, + { .name = "chunk-types", .has_arg = 1, .val = '3' }, + { .name = NULL } +}; + +static void +parse_sctp_ports(const char *portstring, + u_int16_t *ports) +{ + char *buffer; + char *cp; + + buffer = strdup(portstring); + DEBUGP("%s\n", portstring); + if ((cp = strchr(buffer, ':')) == NULL) { + ports[0] = ports[1] = xtables_parse_port(buffer, "sctp"); + } + else { + *cp = '\0'; + cp++; + + ports[0] = buffer[0] ? xtables_parse_port(buffer, "sctp") : 0; + ports[1] = cp[0] ? xtables_parse_port(cp, "sctp") : 0xFFFF; + + if (ports[0] > ports[1]) + xtables_error(PARAMETER_PROBLEM, + "invalid portrange (min > max)"); + } + free(buffer); +} + +struct sctp_chunk_names { + const char *name; + unsigned int chunk_type; + const char *valid_flags; +}; + +/*'ALL' and 'NONE' will be treated specially. */ +static const struct sctp_chunk_names sctp_chunk_names[] += { { .name = "DATA", .chunk_type = 0, .valid_flags = "----IUBE"}, + { .name = "INIT", .chunk_type = 1, .valid_flags = "--------"}, + { .name = "INIT_ACK", .chunk_type = 2, .valid_flags = "--------"}, + { .name = "SACK", .chunk_type = 3, .valid_flags = "--------"}, + { .name = "HEARTBEAT", .chunk_type = 4, .valid_flags = "--------"}, + { .name = "HEARTBEAT_ACK", .chunk_type = 5, .valid_flags = "--------"}, + { .name = "ABORT", .chunk_type = 6, .valid_flags = "-------T"}, + { .name = "SHUTDOWN", .chunk_type = 7, .valid_flags = "--------"}, + { .name = "SHUTDOWN_ACK", .chunk_type = 8, .valid_flags = "--------"}, + { .name = "ERROR", .chunk_type = 9, .valid_flags = "--------"}, + { .name = "COOKIE_ECHO", .chunk_type = 10, .valid_flags = "--------"}, + { .name = "COOKIE_ACK", .chunk_type = 11, .valid_flags = "--------"}, + { .name = "ECN_ECNE", .chunk_type = 12, .valid_flags = "--------"}, + { .name = "ECN_CWR", .chunk_type = 13, .valid_flags = "--------"}, + { .name = "SHUTDOWN_COMPLETE", .chunk_type = 14, .valid_flags = "-------T"}, + { .name = "ASCONF", .chunk_type = 193, .valid_flags = "--------"}, + { .name = "ASCONF_ACK", .chunk_type = 128, .valid_flags = "--------"}, + { .name = "FORWARD_TSN", .chunk_type = 192, .valid_flags = "--------"}, +}; + +static void +save_chunk_flag_info(struct xt_sctp_flag_info *flag_info, + int *flag_count, + int chunktype, + int bit, + int set) +{ + int i; + + for (i = 0; i < *flag_count; i++) { + if (flag_info[i].chunktype == chunktype) { + DEBUGP("Previous match found\n"); + flag_info[i].chunktype = chunktype; + flag_info[i].flag_mask |= (1 << bit); + if (set) { + flag_info[i].flag |= (1 << bit); + } + + return; + } + } + + if (*flag_count == XT_NUM_SCTP_FLAGS) { + xtables_error (PARAMETER_PROBLEM, + "Number of chunk types with flags exceeds currently allowed limit." + "Increasing this limit involves changing IPT_NUM_SCTP_FLAGS and" + "recompiling both the kernel space and user space modules\n"); + } + + flag_info[*flag_count].chunktype = chunktype; + flag_info[*flag_count].flag_mask |= (1 << bit); + if (set) { + flag_info[*flag_count].flag |= (1 << bit); + } + (*flag_count)++; +} + +static void +parse_sctp_chunk(struct xt_sctp_info *einfo, + const char *chunks) +{ + char *ptr; + char *buffer; + unsigned int i, j; + int found = 0; + char *chunk_flags; + + buffer = strdup(chunks); + DEBUGP("Buffer: %s\n", buffer); + + SCTP_CHUNKMAP_RESET(einfo->chunkmap); + + if (!strcasecmp(buffer, "ALL")) { + SCTP_CHUNKMAP_SET_ALL(einfo->chunkmap); + goto out; + } + + if (!strcasecmp(buffer, "NONE")) { + SCTP_CHUNKMAP_RESET(einfo->chunkmap); + goto out; + } + + for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) { + found = 0; + DEBUGP("Next Chunk type %s\n", ptr); + + if ((chunk_flags = strchr(ptr, ':')) != NULL) { + *chunk_flags++ = 0; + } + + for (i = 0; i < ARRAY_SIZE(sctp_chunk_names); ++i) + if (strcasecmp(sctp_chunk_names[i].name, ptr) == 0) { + DEBUGP("Chunk num %d\n", sctp_chunk_names[i].chunk_type); + SCTP_CHUNKMAP_SET(einfo->chunkmap, + sctp_chunk_names[i].chunk_type); + found = 1; + break; + } + if (!found) + xtables_error(PARAMETER_PROBLEM, + "Unknown sctp chunk `%s'", ptr); + + if (chunk_flags) { + DEBUGP("Chunk flags %s\n", chunk_flags); + for (j = 0; j < strlen(chunk_flags); j++) { + char *p; + int bit; + + if ((p = strchr(sctp_chunk_names[i].valid_flags, + toupper(chunk_flags[j]))) != NULL) { + bit = p - sctp_chunk_names[i].valid_flags; + bit = 7 - bit; + + save_chunk_flag_info(einfo->flag_info, + &(einfo->flag_count), i, bit, + isupper(chunk_flags[j])); + } else { + xtables_error(PARAMETER_PROBLEM, + "Invalid flags for chunk type %d\n", i); + } + } + } + } +out: + free(buffer); +} + +static void +parse_sctp_chunks(struct xt_sctp_info *einfo, + const char *match_type, + const char *chunks) +{ + DEBUGP("Match type: %s Chunks: %s\n", match_type, chunks); + if (!strcasecmp(match_type, "ANY")) { + einfo->chunk_match_type = SCTP_CHUNK_MATCH_ANY; + } else if (!strcasecmp(match_type, "ALL")) { + einfo->chunk_match_type = SCTP_CHUNK_MATCH_ALL; + } else if (!strcasecmp(match_type, "ONLY")) { + einfo->chunk_match_type = SCTP_CHUNK_MATCH_ONLY; + } else { + xtables_error (PARAMETER_PROBLEM, + "Match type has to be one of \"ALL\", \"ANY\" or \"ONLY\""); + } + + SCTP_CHUNKMAP_RESET(einfo->chunkmap); + parse_sctp_chunk(einfo, chunks); +} + +static int +sctp_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_sctp_info *einfo + = (struct xt_sctp_info *)(*match)->data; + + switch (c) { + case '1': + if (*flags & XT_SCTP_SRC_PORTS) + xtables_error(PARAMETER_PROBLEM, + "Only one `--source-port' allowed"); + einfo->flags |= XT_SCTP_SRC_PORTS; + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_sctp_ports(optarg, einfo->spts); + if (invert) + einfo->invflags |= XT_SCTP_SRC_PORTS; + *flags |= XT_SCTP_SRC_PORTS; + break; + + case '2': + if (*flags & XT_SCTP_DEST_PORTS) + xtables_error(PARAMETER_PROBLEM, + "Only one `--destination-port' allowed"); + einfo->flags |= XT_SCTP_DEST_PORTS; + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_sctp_ports(optarg, einfo->dpts); + if (invert) + einfo->invflags |= XT_SCTP_DEST_PORTS; + *flags |= XT_SCTP_DEST_PORTS; + break; + + case '3': + if (*flags & XT_SCTP_CHUNK_TYPES) + xtables_error(PARAMETER_PROBLEM, + "Only one `--chunk-types' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + if (!argv[optind] + || argv[optind][0] == '-' || argv[optind][0] == '!') + xtables_error(PARAMETER_PROBLEM, + "--chunk-types requires two args"); + + einfo->flags |= XT_SCTP_CHUNK_TYPES; + parse_sctp_chunks(einfo, optarg, argv[optind]); + if (invert) + einfo->invflags |= XT_SCTP_CHUNK_TYPES; + optind++; + *flags |= XT_SCTP_CHUNK_TYPES; + break; + + default: + return 0; + } + return 1; +} + +static char * +port_to_service(int port) +{ + struct servent *service; + + if ((service = getservbyport(htons(port), "sctp"))) + return service->s_name; + + return NULL; +} + +static void +print_port(u_int16_t port, int numeric) +{ + char *service; + + if (numeric || (service = port_to_service(port)) == NULL) + printf("%u", port); + else + printf("%s", service); +} + +static void +print_ports(const char *name, u_int16_t min, u_int16_t max, + int invert, int numeric) +{ + const char *inv = invert ? "!" : ""; + + if (min != 0 || max != 0xFFFF || invert) { + printf("%s", name); + if (min == max) { + printf(":%s", inv); + print_port(min, numeric); + } else { + printf("s:%s", inv); + print_port(min, numeric); + printf(":"); + print_port(max, numeric); + } + printf(" "); + } +} + +static void +print_chunk_flags(u_int32_t chunknum, u_int8_t chunk_flags, u_int8_t chunk_flags_mask) +{ + int i; + + DEBUGP("type: %d\tflags: %x\tflag mask: %x\n", chunknum, chunk_flags, + chunk_flags_mask); + + if (chunk_flags_mask) { + printf(":"); + } + + for (i = 7; i >= 0; i--) { + if (chunk_flags_mask & (1 << i)) { + if (chunk_flags & (1 << i)) { + printf("%c", sctp_chunk_names[chunknum].valid_flags[7-i]); + } else { + printf("%c", tolower(sctp_chunk_names[chunknum].valid_flags[7-i])); + } + } + } +} + +static void +print_chunk(u_int32_t chunknum, int numeric) +{ + if (numeric) { + printf("0x%04X", chunknum); + } + else { + int i; + + for (i = 0; i < ARRAY_SIZE(sctp_chunk_names); ++i) + if (sctp_chunk_names[i].chunk_type == chunknum) + printf("%s", sctp_chunk_names[chunknum].name); + } +} + +static void +print_chunks(const struct xt_sctp_info *einfo, int numeric) +{ + u_int32_t chunk_match_type = einfo->chunk_match_type; + const struct xt_sctp_flag_info *flag_info = einfo->flag_info; + int flag_count = einfo->flag_count; + int i, j; + int flag; + + switch (chunk_match_type) { + case SCTP_CHUNK_MATCH_ANY: printf("any "); break; + case SCTP_CHUNK_MATCH_ALL: printf("all "); break; + case SCTP_CHUNK_MATCH_ONLY: printf("only "); break; + default: printf("Never reach herer\n"); break; + } + + if (SCTP_CHUNKMAP_IS_CLEAR(einfo->chunkmap)) { + printf("NONE "); + goto out; + } + + if (SCTP_CHUNKMAP_IS_ALL_SET(einfo->chunkmap)) { + printf("ALL "); + goto out; + } + + flag = 0; + for (i = 0; i < 256; i++) { + if (SCTP_CHUNKMAP_IS_SET(einfo->chunkmap, i)) { + if (flag) + printf(","); + flag = 1; + print_chunk(i, numeric); + for (j = 0; j < flag_count; j++) { + if (flag_info[j].chunktype == i) { + print_chunk_flags(i, flag_info[j].flag, + flag_info[j].flag_mask); + } + } + } + } + + if (flag) + printf(" "); +out: + return; +} + +static void +sctp_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_sctp_info *einfo = + (const struct xt_sctp_info *)match->data; + + printf("sctp "); + + if (einfo->flags & XT_SCTP_SRC_PORTS) { + print_ports("spt", einfo->spts[0], einfo->spts[1], + einfo->invflags & XT_SCTP_SRC_PORTS, + numeric); + } + + if (einfo->flags & XT_SCTP_DEST_PORTS) { + print_ports("dpt", einfo->dpts[0], einfo->dpts[1], + einfo->invflags & XT_SCTP_DEST_PORTS, + numeric); + } + + if (einfo->flags & XT_SCTP_CHUNK_TYPES) { + /* FIXME: print_chunks() is used in save() where the printing of '!' + s taken care of, so we need to do that here as well */ + if (einfo->invflags & XT_SCTP_CHUNK_TYPES) { + printf("! "); + } + print_chunks(einfo, numeric); + } +} + +static void sctp_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_sctp_info *einfo = + (const struct xt_sctp_info *)match->data; + + if (einfo->flags & XT_SCTP_SRC_PORTS) { + if (einfo->invflags & XT_SCTP_SRC_PORTS) + printf("! "); + if (einfo->spts[0] != einfo->spts[1]) + printf("--sport %u:%u ", + einfo->spts[0], einfo->spts[1]); + else + printf("--sport %u ", einfo->spts[0]); + } + + if (einfo->flags & XT_SCTP_DEST_PORTS) { + if (einfo->invflags & XT_SCTP_DEST_PORTS) + printf("! "); + if (einfo->dpts[0] != einfo->dpts[1]) + printf("--dport %u:%u ", + einfo->dpts[0], einfo->dpts[1]); + else + printf("--dport %u ", einfo->dpts[0]); + } + + if (einfo->flags & XT_SCTP_CHUNK_TYPES) { + if (einfo->invflags & XT_SCTP_CHUNK_TYPES) + printf("! "); + printf("--chunk-types "); + + print_chunks(einfo, 0); + } +} + +static struct xtables_match sctp_match = { + .name = "sctp", + .family = NFPROTO_UNSPEC, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_sctp_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_sctp_info)), + .help = sctp_help, + .init = sctp_init, + .parse = sctp_parse, + .print = sctp_print, + .save = sctp_save, + .extra_opts = sctp_opts, +}; + +void _init(void) +{ + xtables_register_match(&sctp_match); +} diff --git a/extensions/libxt_sctp.man b/extensions/libxt_sctp.man new file mode 100644 index 0000000..9c0bd8c --- /dev/null +++ b/extensions/libxt_sctp.man @@ -0,0 +1,28 @@ +.TP +[\fB!\fP] \fB\-\-source\-port\fP,\fB\-\-sport\fP \fIport\fP[\fB:\fP\fIport\fP] +.TP +[\fB!\fP] \fB\-\-destination\-port\fP,\fB\-\-dport\fP \fIport\fP[\fB:\fP\fIport\fP] +.TP +[\fB!\fP] \fB\-\-chunk\-types\fP {\fBall\fP|\fBany\fP|\fBonly\fP} \fIchunktype\fP[\fB:\fP\fIflags\fP] [...] +The flag letter in upper case indicates that the flag is to match if set, +in the lower case indicates to match if unset. + +Chunk types: DATA INIT INIT_ACK SACK HEARTBEAT HEARTBEAT_ACK ABORT SHUTDOWN SHUTDOWN_ACK ERROR COOKIE_ECHO COOKIE_ACK ECN_ECNE ECN_CWR SHUTDOWN_COMPLETE ASCONF ASCONF_ACK FORWARD_TSN + +chunk type available flags +.br +DATA I U B E i u b e +.br +ABORT T t +.br +SHUTDOWN_COMPLETE T t + +(lowercase means flag should be "off", uppercase means "on") +.P +Examples: + +iptables \-A INPUT \-p sctp \-\-dport 80 \-j DROP + +iptables \-A INPUT \-p sctp \-\-chunk\-types any DATA,INIT \-j DROP + +iptables \-A INPUT \-p sctp \-\-chunk\-types any DATA:Be \-j ACCEPT diff --git a/extensions/libxt_set.c b/extensions/libxt_set.c new file mode 100644 index 0000000..75fa3c2 --- /dev/null +++ b/extensions/libxt_set.c @@ -0,0 +1,250 @@ +/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> + * Patrick Schaaf <bof@bof.de> + * Martin Josefsson <gandalf@wlug.westbo.se> + * Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Shared library add-on to iptables to add IP set matching. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <ctype.h> +#include <errno.h> + +#include <xtables.h> +#include <linux/netfilter/xt_set.h> +#include "libxt_set.h" + +static void +set_help(void) +{ + printf("set match options:\n" + " [!] --match-set name flags\n" + " 'name' is the set name from to match,\n" + " 'flags' are the comma separated list of\n" + " 'src' and 'dst' specifications.\n"); +} + +static const struct option set_opts[] = { + { .name = "match-set", .has_arg = true, .val = '1'}, + { .name = "set", .has_arg = true, .val = '2'}, + { .name = NULL } +}; + +static void +set_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "You must specify `--match-set' with proper arguments"); +} + +static int +set_parse_v0(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_set_info_match_v0 *myinfo = + (struct xt_set_info_match_v0 *) (*match)->data; + struct xt_set_info_v0 *info = &myinfo->match_set; + + switch (c) { + case '2': + fprintf(stderr, + "--set option deprecated, please use --match-set\n"); + case '1': /* --match-set <set> <flag>[,<flag> */ + if (info->u.flags[0]) + xtables_error(PARAMETER_PROBLEM, + "--match-set can be specified only once"); + + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) + info->u.flags[0] |= IPSET_MATCH_INV; + + if (!argv[optind] + || argv[optind][0] == '-' + || argv[optind][0] == '!') + xtables_error(PARAMETER_PROBLEM, + "--match-set requires two args."); + + if (strlen(optarg) > IPSET_MAXNAMELEN - 1) + xtables_error(PARAMETER_PROBLEM, + "setname `%s' too long, max %d characters.", + optarg, IPSET_MAXNAMELEN - 1); + + get_set_byname(optarg, (struct xt_set_info *)info); + parse_dirs_v0(argv[optind], info); + DEBUGP("parse: set index %u\n", info->index); + optind++; + + *flags = 1; + break; + + default: + return 0; + } + + return 1; +} + +static void +print_match_v0(const char *prefix, const struct xt_set_info_v0 *info) +{ + int i; + char setname[IPSET_MAXNAMELEN]; + + get_set_byid(setname, info->index); + printf("%s%s %s", + (info->u.flags[0] & IPSET_MATCH_INV) ? "! " : "", + prefix, + setname); + for (i = 0; i < IPSET_DIM_MAX; i++) { + if (!info->u.flags[i]) + break; + printf("%s%s", + i == 0 ? " " : ",", + info->u.flags[i] & IPSET_SRC ? "src" : "dst"); + } + printf(" "); +} + +/* Prints out the matchinfo. */ +static void +set_print_v0(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_set_info_match_v0 *info = (const void *)match->data; + + print_match_v0("match-set", &info->match_set); +} + +static void +set_save_v0(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_set_info_match_v0 *info = (const void *)match->data; + + print_match_v0("--match-set", &info->match_set); +} + +static int +set_parse(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 *info = &myinfo->match_set; + + switch (c) { + case '2': + fprintf(stderr, + "--set option deprecated, please use --match-set\n"); + case '1': /* --match-set <set> <flag>[,<flag> */ + if (info->dim) + xtables_error(PARAMETER_PROBLEM, + "--match-set can be specified only once"); + + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + if (invert) + info->flags |= IPSET_INV_MATCH; + + if (!argv[optind] + || argv[optind][0] == '-' + || argv[optind][0] == '!') + xtables_error(PARAMETER_PROBLEM, + "--match-set requires two args."); + + if (strlen(optarg) > IPSET_MAXNAMELEN - 1) + xtables_error(PARAMETER_PROBLEM, + "setname `%s' too long, max %d characters.", + optarg, IPSET_MAXNAMELEN - 1); + + get_set_byname(optarg, info); + parse_dirs(argv[optind], info); + DEBUGP("parse: set index %u\n", info->index); + optind++; + + *flags = 1; + break; + + default: + return 0; + } + + return 1; +} + +static void +print_match(const char *prefix, const struct xt_set_info *info) +{ + int i; + char setname[IPSET_MAXNAMELEN]; + + get_set_byid(setname, info->index); + printf("%s%s %s", + (info->flags & IPSET_INV_MATCH) ? "! " : "", + prefix, + setname); + for (i = 1; i <= info->dim; i++) { + printf("%s%s", + i == 1 ? " " : ",", + info->flags & (1 << i) ? "src" : "dst"); + } + printf(" "); +} + +/* Prints out the matchinfo. */ +static void +set_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_set_info_match *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) +{ + const struct xt_set_info_match *info = (const void *)match->data; + + print_match("--match-set", &info->match_set); +} + +static struct xtables_match set_mt_reg[] = { + { + .name = "set", + .revision = 0, + .version = XTABLES_VERSION, + .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, + .parse = set_parse_v0, + .final_check = set_check, + .print = set_print_v0, + .save = set_save_v0, + .extra_opts = set_opts, + }, + { + .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, + }, +}; + +void _init(void) +{ + xtables_register_matches(set_mt_reg, ARRAY_SIZE(set_mt_reg)); +} diff --git a/extensions/libxt_set.h b/extensions/libxt_set.h new file mode 100644 index 0000000..6b93691 --- /dev/null +++ b/extensions/libxt_set.h @@ -0,0 +1,147 @@ +#ifndef _LIBXT_SET_H +#define _LIBXT_SET_H + +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <errno.h> + +#ifdef DEBUG +#define DEBUGP(x, args...) fprintf(stderr, x , ## args) +#else +#define DEBUGP(x, args...) +#endif + +static int +get_version(unsigned *version) +{ + int res, sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + struct ip_set_req_version req_version; + socklen_t size = sizeof(req_version); + + if (sockfd < 0) + xtables_error(OTHER_PROBLEM, + "Can't open socket to ipset.\n"); + + req_version.op = IP_SET_OP_VERSION; + res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req_version, &size); + if (res != 0) + xtables_error(OTHER_PROBLEM, + "Kernel module xt_set is not loaded in.\n"); + + *version = req_version.version; + + return sockfd; +} + +static void +get_set_byid(char *setname, ip_set_id_t idx) +{ + struct ip_set_req_get_set req; + socklen_t size = sizeof(struct ip_set_req_get_set); + int res, sockfd; + + sockfd = get_version(&req.version); + req.op = IP_SET_OP_GET_BYINDEX; + req.set.index = idx; + res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req, &size); + close(sockfd); + + if (res != 0) + xtables_error(OTHER_PROBLEM, + "Problem when communicating with ipset, errno=%d.\n", + errno); + if (size != sizeof(struct ip_set_req_get_set)) + xtables_error(OTHER_PROBLEM, + "Incorrect return size from kernel during ipset lookup, " + "(want %zu, got %zu)\n", + sizeof(struct ip_set_req_get_set), (size_t)size); + if (req.set.name[0] == '\0') + xtables_error(PARAMETER_PROBLEM, + "Set with index %i in kernel doesn't exist.\n", idx); + + strncpy(setname, req.set.name, IPSET_MAXNAMELEN); +} + +static void +get_set_byname(const char *setname, struct xt_set_info *info) +{ + struct ip_set_req_get_set req; + socklen_t size = sizeof(struct ip_set_req_get_set); + int res, sockfd; + + sockfd = get_version(&req.version); + req.op = IP_SET_OP_GET_BYNAME; + strncpy(req.set.name, setname, IPSET_MAXNAMELEN); + req.set.name[IPSET_MAXNAMELEN - 1] = '\0'; + res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req, &size); + close(sockfd); + + if (res != 0) + xtables_error(OTHER_PROBLEM, + "Problem when communicating with ipset, errno=%d.\n", + errno); + if (size != sizeof(struct ip_set_req_get_set)) + xtables_error(OTHER_PROBLEM, + "Incorrect return size from kernel during ipset lookup, " + "(want %zu, got %zu)\n", + sizeof(struct ip_set_req_get_set), (size_t)size); + if (req.set.index == IPSET_INVALID_ID) + xtables_error(PARAMETER_PROBLEM, + "Set %s doesn't exist.\n", setname); + + info->index = req.set.index; +} + +static void +parse_dirs_v0(const char *opt_arg, struct xt_set_info_v0 *info) +{ + char *saved = strdup(opt_arg); + char *ptr, *tmp = saved; + int i = 0; + + while (i < (IPSET_DIM_MAX - 1) && tmp != NULL) { + ptr = strsep(&tmp, ","); + if (strncmp(ptr, "src", 3) == 0) + info->u.flags[i++] |= IPSET_SRC; + else if (strncmp(ptr, "dst", 3) == 0) + info->u.flags[i++] |= IPSET_DST; + else + xtables_error(PARAMETER_PROBLEM, + "You must spefify (the comma separated list of) 'src' or 'dst'."); + } + + if (tmp) + xtables_error(PARAMETER_PROBLEM, + "Can't be more src/dst options than %i.", + IPSET_DIM_MAX - 1); + + free(saved); +} + +static void +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) { + info->dim++; + ptr = strsep(&tmp, ","); + if (strncmp(ptr, "src", 3) == 0) + info->flags |= (1 << info->dim); + else if (strncmp(ptr, "dst", 3) != 0) + xtables_error(PARAMETER_PROBLEM, + "You must spefify (the comma separated list of) 'src' or 'dst'."); + } + + if (tmp) + xtables_error(PARAMETER_PROBLEM, + "Can't be more src/dst options than %i.", + IPSET_DIM_MAX - 1); + + free(saved); +} + +#endif /*_LIBXT_SET_H*/ diff --git a/extensions/libxt_set.man b/extensions/libxt_set.man new file mode 100644 index 0000000..aca1bfc --- /dev/null +++ b/extensions/libxt_set.man @@ -0,0 +1,23 @@ +This module matches IP sets which can be defined by ipset(8). +.TP +[\fB!\fP] \fB\-\-match\-set\fP \fIsetname\fP \fIflag\fP[\fB,\fP\fIflag\fP]... +where flags are the comma separated list of +.BR "src" +and/or +.BR "dst" +specifications and there can be no more than six of them. Hence the command +.IP + iptables \-A FORWARD \-m set \-\-match\-set test src,dst +.IP +will match packets, for which (if the set type is ipportmap) the source +address and destination port pair can be found in the specified set. If +the set type of the specified set is single dimension (for example ipmap), +then the command will match packets for which the source address can be +found in the specified set. +.PP +The option \fB\-\-match\-set\fR can be replaced by \fB\-\-set\fR if that does +not clash with an option of other extensions. +.PP +Use of -m set requires that ipset kernel support is provided. As standard +kernels do not ship this currently, the ipset or Xtables-addons package needs +to be installed. diff --git a/extensions/libxt_socket.c b/extensions/libxt_socket.c new file mode 100644 index 0000000..1490473 --- /dev/null +++ b/extensions/libxt_socket.c @@ -0,0 +1,19 @@ +/* + * Shared library add-on to iptables to add early socket matching support. + * + * Copyright (C) 2007 BalaBit IT Ltd. + */ +#include <xtables.h> + +static struct xtables_match socket_mt_reg = { + .name = "socket", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(0), + .userspacesize = XT_ALIGN(0), +}; + +void _init(void) +{ + xtables_register_match(&socket_mt_reg); +} diff --git a/extensions/libxt_socket.man b/extensions/libxt_socket.man new file mode 100644 index 0000000..50c8854 --- /dev/null +++ b/extensions/libxt_socket.man @@ -0,0 +1,2 @@ +This matches if an open socket can be found by doing a socket lookup on the +packet. diff --git a/extensions/libxt_standard.c b/extensions/libxt_standard.c new file mode 100644 index 0000000..c64ba29 --- /dev/null +++ b/extensions/libxt_standard.c @@ -0,0 +1,24 @@ +/* Shared library add-on to iptables for standard target support. */ +#include <stdio.h> +#include <xtables.h> + +static void standard_help(void) +{ + printf( +"standard match options:\n" +"(If target is DROP, ACCEPT, RETURN or nothing)\n"); +} + +static struct xtables_target standard_target = { + .family = NFPROTO_UNSPEC, + .name = "standard", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(int)), + .userspacesize = XT_ALIGN(sizeof(int)), + .help = standard_help, +}; + +void _init(void) +{ + xtables_register_target(&standard_target); +} diff --git a/extensions/libxt_state.c b/extensions/libxt_state.c new file mode 100644 index 0000000..d8159e5 --- /dev/null +++ b/extensions/libxt_state.c @@ -0,0 +1,158 @@ +/* Shared library add-on to iptables to add state tracking support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <linux/netfilter/nf_conntrack_common.h> +#include <linux/netfilter/xt_state.h> + +#ifndef XT_STATE_UNTRACKED +#define XT_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 1)) +#endif + +static void +state_help(void) +{ + printf( +"state match options:\n" +" [!] --state [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED][,...]\n" +" State(s) to match\n"); +} + +static const struct option state_opts[] = { + { "state", 1, NULL, '1' }, + { .name = NULL } +}; + +static int +state_parse_state(const char *state, size_t len, struct xt_state_info *sinfo) +{ + if (strncasecmp(state, "INVALID", len) == 0) + sinfo->statemask |= XT_STATE_INVALID; + else if (strncasecmp(state, "NEW", len) == 0) + sinfo->statemask |= XT_STATE_BIT(IP_CT_NEW); + else if (strncasecmp(state, "ESTABLISHED", len) == 0) + sinfo->statemask |= XT_STATE_BIT(IP_CT_ESTABLISHED); + else if (strncasecmp(state, "RELATED", len) == 0) + sinfo->statemask |= XT_STATE_BIT(IP_CT_RELATED); + else if (strncasecmp(state, "UNTRACKED", len) == 0) + sinfo->statemask |= XT_STATE_UNTRACKED; + else + return 0; + return 1; +} + +static void +state_parse_states(const char *arg, struct xt_state_info *sinfo) +{ + const char *comma; + + while ((comma = strchr(arg, ',')) != NULL) { + if (comma == arg || !state_parse_state(arg, comma-arg, sinfo)) + xtables_error(PARAMETER_PROBLEM, "Bad state \"%s\"", arg); + arg = comma+1; + } + if (!*arg) + xtables_error(PARAMETER_PROBLEM, "\"--state\" requires a list of " + "states with no spaces, e.g. " + "ESTABLISHED,RELATED"); + if (strlen(arg) == 0 || !state_parse_state(arg, strlen(arg), sinfo)) + xtables_error(PARAMETER_PROBLEM, "Bad state \"%s\"", arg); +} + +static int +state_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, + struct xt_entry_match **match) +{ + struct xt_state_info *sinfo = (struct xt_state_info *)(*match)->data; + + switch (c) { + case '1': + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + state_parse_states(optarg, sinfo); + if (invert) + sinfo->statemask = ~sinfo->statemask; + *flags = 1; + break; + + default: + return 0; + } + + return 1; +} + +static void state_final_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, "You must specify \"--state\""); +} + +static void state_print_state(unsigned int statemask) +{ + const char *sep = ""; + + if (statemask & XT_STATE_INVALID) { + printf("%sINVALID", sep); + sep = ","; + } + if (statemask & XT_STATE_BIT(IP_CT_NEW)) { + printf("%sNEW", sep); + sep = ","; + } + if (statemask & XT_STATE_BIT(IP_CT_RELATED)) { + printf("%sRELATED", sep); + sep = ","; + } + if (statemask & XT_STATE_BIT(IP_CT_ESTABLISHED)) { + printf("%sESTABLISHED", sep); + sep = ","; + } + if (statemask & XT_STATE_UNTRACKED) { + printf("%sUNTRACKED", sep); + sep = ","; + } + printf(" "); +} + +static void +state_print(const void *ip, + const struct xt_entry_match *match, + int numeric) +{ + const struct xt_state_info *sinfo = (const void *)match->data; + + printf("state "); + state_print_state(sinfo->statemask); +} + +static void state_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_state_info *sinfo = (const void *)match->data; + + printf("--state "); + state_print_state(sinfo->statemask); +} + +static struct xtables_match state_match = { + .family = NFPROTO_UNSPEC, + .name = "state", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_state_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_state_info)), + .help = state_help, + .parse = state_parse, + .final_check = state_final_check, + .print = state_print, + .save = state_save, + .extra_opts = state_opts, +}; + +void _init(void) +{ + xtables_register_match(&state_match); +} diff --git a/extensions/libxt_state.man b/extensions/libxt_state.man new file mode 100644 index 0000000..37d095b --- /dev/null +++ b/extensions/libxt_state.man @@ -0,0 +1,24 @@ +This module, when combined with connection tracking, allows access to +the connection tracking state for this packet. +.TP +[\fB!\fP] \fB\-\-state\fP \fIstate\fP +Where state is a comma separated list of the connection states to +match. Possible states are +.B INVALID +meaning that the packet could not be identified for some reason which +includes running out of memory and ICMP errors which don't correspond to any +known connection, +.B ESTABLISHED +meaning that the packet is associated with a connection which has seen +packets in both directions, +.B NEW +meaning that the packet has started a new connection, or otherwise +associated with a connection which has not seen packets in both +directions, and +.B RELATED +meaning that the packet is starting a new connection, but is +associated with an existing connection, such as an FTP data transfer, +or an ICMP error. +.B UNTRACKED +meaning that the packet is not tracked at all, which happens if you use +the NOTRACK target in raw table. diff --git a/extensions/libxt_statistic.c b/extensions/libxt_statistic.c new file mode 100644 index 0000000..913aa2c --- /dev/null +++ b/extensions/libxt_statistic.c @@ -0,0 +1,180 @@ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/xt_statistic.h> + +static void statistic_help(void) +{ + printf( +"statistic match options:\n" +" --mode mode Match mode (random, nth)\n" +" random mode:\n" +" --probability p Probability\n" +" nth mode:\n" +" --every n Match every nth packet\n" +" --packet p Initial counter value (0 <= p <= n-1, default 0)\n"); +} + +static const struct option statistic_opts[] = { + { "mode", 1, NULL, '1' }, + { "probability", 1, NULL, '2' }, + { "every", 1, NULL, '3' }, + { "packet", 1, NULL, '4' }, + { .name = NULL } +}; + +static struct xt_statistic_info *global_info; + +static void statistic_mt_init(struct xt_entry_match *match) +{ + global_info = (void *)match->data; +} + +static int +statistic_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_statistic_info *info = (void *)(*match)->data; + unsigned int val; + double prob; + + if (invert) + info->flags |= XT_STATISTIC_INVERT; + + switch (c) { + case '1': + if (*flags & 0x1) + xtables_error(PARAMETER_PROBLEM, "double --mode"); + if (!strcmp(optarg, "random")) + info->mode = XT_STATISTIC_MODE_RANDOM; + else if (!strcmp(optarg, "nth")) + info->mode = XT_STATISTIC_MODE_NTH; + else + xtables_error(PARAMETER_PROBLEM, "Bad mode \"%s\"", optarg); + *flags |= 0x1; + break; + case '2': + if (*flags & 0x2) + xtables_error(PARAMETER_PROBLEM, "double --probability"); + prob = atof(optarg); + if (prob < 0 || prob > 1) + xtables_error(PARAMETER_PROBLEM, + "--probability must be between 0 and 1"); + info->u.random.probability = 0x80000000 * prob; + *flags |= 0x2; + break; + case '3': + if (*flags & 0x4) + xtables_error(PARAMETER_PROBLEM, "double --every"); + if (!xtables_strtoui(optarg, NULL, &val, 0, UINT32_MAX)) + xtables_error(PARAMETER_PROBLEM, + "cannot parse --every `%s'", optarg); + info->u.nth.every = val; + if (info->u.nth.every == 0) + xtables_error(PARAMETER_PROBLEM, "--every cannot be 0"); + info->u.nth.every--; + *flags |= 0x4; + break; + case '4': + if (*flags & 0x8) + xtables_error(PARAMETER_PROBLEM, "double --packet"); + if (!xtables_strtoui(optarg, NULL, &val, 0, UINT32_MAX)) + xtables_error(PARAMETER_PROBLEM, + "cannot parse --packet `%s'", optarg); + info->u.nth.packet = val; + *flags |= 0x8; + break; + default: + return 0; + } + return 1; +} + +static void statistic_check(unsigned int flags) +{ + if (!(flags & 0x1)) + xtables_error(PARAMETER_PROBLEM, "no mode specified"); + if ((flags & 0x2) && (flags & (0x4 | 0x8))) + xtables_error(PARAMETER_PROBLEM, + "both nth and random parameters given"); + if (flags & 0x2 && global_info->mode != XT_STATISTIC_MODE_RANDOM) + xtables_error(PARAMETER_PROBLEM, + "--probability can only be used in random mode"); + if (flags & 0x4 && global_info->mode != XT_STATISTIC_MODE_NTH) + xtables_error(PARAMETER_PROBLEM, + "--every can only be used in nth mode"); + if (flags & 0x8 && global_info->mode != XT_STATISTIC_MODE_NTH) + xtables_error(PARAMETER_PROBLEM, + "--packet can only be used in nth mode"); + if ((flags & 0x8) && !(flags & 0x4)) + xtables_error(PARAMETER_PROBLEM, + "--packet can only be used with --every"); + /* at this point, info->u.nth.every have been decreased. */ + if (global_info->u.nth.packet > global_info->u.nth.every) + xtables_error(PARAMETER_PROBLEM, + "the --packet p must be 0 <= p <= n-1"); + + + global_info->u.nth.count = global_info->u.nth.every - + global_info->u.nth.packet; +} + +static void print_match(const struct xt_statistic_info *info, char *prefix) +{ + if (info->flags & XT_STATISTIC_INVERT) + printf("! "); + + switch (info->mode) { + case XT_STATISTIC_MODE_RANDOM: + printf("%smode random %sprobability %f ", prefix, prefix, + 1.0 * info->u.random.probability / 0x80000000); + break; + case XT_STATISTIC_MODE_NTH: + printf("%smode nth %severy %u ", prefix, prefix, + info->u.nth.every + 1); + if (info->u.nth.packet) + printf("%spacket %u ", prefix, info->u.nth.packet); + break; + } +} + +static void +statistic_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_statistic_info *info = (const void *)match->data; + + printf("statistic "); + print_match(info, ""); +} + +static void statistic_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_statistic_info *info = (const void *)match->data; + + print_match(info, "--"); +} + +static struct xtables_match statistic_match = { + .family = NFPROTO_UNSPEC, + .name = "statistic", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_statistic_info)), + .userspacesize = offsetof(struct xt_statistic_info, u.nth.count), + .init = statistic_mt_init, + .help = statistic_help, + .parse = statistic_parse, + .final_check = statistic_check, + .print = statistic_print, + .save = statistic_save, + .extra_opts = statistic_opts, +}; + +void _init(void) +{ + xtables_register_match(&statistic_match); +} diff --git a/extensions/libxt_statistic.man b/extensions/libxt_statistic.man new file mode 100644 index 0000000..8fc3b29 --- /dev/null +++ b/extensions/libxt_statistic.man @@ -0,0 +1,30 @@ +This module matches packets based on some statistic condition. +It supports two distinct modes settable with the +\fB\-\-mode\fP +option. +.PP +Supported options: +.TP +\fB\-\-mode\fP \fImode\fP +Set the matching mode of the matching rule, supported modes are +.B random +and +.B nth. +.TP +\fB\-\-probability\fP \fIp\fP +Set the probability from 0 to 1 for a packet to be randomly +matched. It works only with the +.B random +mode. +.TP +\fB\-\-every\fP \fIn\fP +Match one packet every nth packet. It works only with the +.B nth +mode (see also the +\fB\-\-packet\fP +option). +.TP +\fB\-\-packet\fP \fIp\fP +Set the initial counter value (0 <= p <= n\-1, default 0) for the +.B nth +mode. diff --git a/extensions/libxt_string.c b/extensions/libxt_string.c new file mode 100644 index 0000000..df6302e --- /dev/null +++ b/extensions/libxt_string.c @@ -0,0 +1,389 @@ +/* Shared library add-on to iptables to add string matching support. + * + * Copyright (C) 2000 Emmanuel Roger <winfield@freegates.be> + * + * 2005-08-05 Pablo Neira Ayuso <pablo@eurodev.net> + * - reimplemented to use new string matching iptables match + * - add functionality to match packets by using window offsets + * - add functionality to select the string matching algorithm + * + * ChangeLog + * 29.12.2003: Michael Rash <mbr@cipherdyne.org> + * Fixed iptables save/restore for ascii strings + * that contain space chars, and hex strings that + * contain embedded NULL chars. Updated to print + * strings in hex mode if any non-printable char + * is contained within the string. + * + * 27.01.2001: Gianni Tedesco <gianni@ecsc.co.uk> + * Changed --tos to --string in save(). Also + * updated to work with slightly modified + * ipt_string_info. + */ +#define _GNU_SOURCE 1 +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <ctype.h> +#include <xtables.h> +#include <stddef.h> +#include <linux/netfilter/xt_string.h> + +static void string_help(void) +{ + printf( +"string match options:\n" +"--from Offset to start searching from\n" +"--to Offset to stop searching\n" +"--algo Algorithm\n" +"--icase Ignore case (default: 0)\n" +"[!] --string string Match a string in a packet\n" +"[!] --hex-string string Match a hex string in a packet\n"); +} + +static const struct option string_opts[] = { + { "from", 1, NULL, '1' }, + { "to", 1, NULL, '2' }, + { "algo", 1, NULL, '3' }, + { "string", 1, NULL, '4' }, + { "hex-string", 1, NULL, '5' }, + { "icase", 0, NULL, '6' }, + { .name = NULL } +}; + +static void string_init(struct xt_entry_match *m) +{ + struct xt_string_info *i = (struct xt_string_info *) m->data; + + if (i->to_offset == 0) + i->to_offset = UINT16_MAX; +} + +static void +parse_string(const char *s, struct xt_string_info *info) +{ + /* xt_string does not need \0 at the end of the pattern */ + if (strlen(s) <= XT_STRING_MAX_PATTERN_SIZE) { + strncpy(info->pattern, s, XT_STRING_MAX_PATTERN_SIZE); + info->patlen = strnlen(s, XT_STRING_MAX_PATTERN_SIZE); + return; + } + xtables_error(PARAMETER_PROBLEM, "STRING too long \"%s\"", s); +} + +static void +parse_algo(const char *s, struct xt_string_info *info) +{ + /* xt_string needs \0 for algo name */ + if (strlen(s) < XT_STRING_MAX_ALGO_NAME_SIZE) { + strncpy(info->algo, s, XT_STRING_MAX_ALGO_NAME_SIZE); + return; + } + xtables_error(PARAMETER_PROBLEM, "ALGO too long \"%s\"", s); +} + +static void +parse_hex_string(const char *s, struct xt_string_info *info) +{ + int i=0, slen, sindex=0, schar; + short hex_f = 0, literal_f = 0; + char hextmp[3]; + + slen = strlen(s); + + if (slen == 0) { + xtables_error(PARAMETER_PROBLEM, + "STRING must contain at least one char"); + } + + while (i < slen) { + if (s[i] == '\\' && !hex_f) { + literal_f = 1; + } else if (s[i] == '\\') { + xtables_error(PARAMETER_PROBLEM, + "Cannot include literals in hex data"); + } else if (s[i] == '|') { + if (hex_f) + hex_f = 0; + else { + hex_f = 1; + /* get past any initial whitespace just after the '|' */ + while (s[i+1] == ' ') + i++; + } + if (i+1 >= slen) + break; + else + i++; /* advance to the next character */ + } + + if (literal_f) { + if (i+1 >= slen) { + xtables_error(PARAMETER_PROBLEM, + "Bad literal placement at end of string"); + } + info->pattern[sindex] = s[i+1]; + i += 2; /* skip over literal char */ + literal_f = 0; + } else if (hex_f) { + if (i+1 >= slen) { + xtables_error(PARAMETER_PROBLEM, + "Odd number of hex digits"); + } + if (i+2 >= slen) { + /* must end with a "|" */ + xtables_error(PARAMETER_PROBLEM, "Invalid hex block"); + } + if (! isxdigit(s[i])) /* check for valid hex char */ + xtables_error(PARAMETER_PROBLEM, "Invalid hex char '%c'", s[i]); + if (! isxdigit(s[i+1])) /* check for valid hex char */ + xtables_error(PARAMETER_PROBLEM, "Invalid hex char '%c'", s[i+1]); + hextmp[0] = s[i]; + hextmp[1] = s[i+1]; + hextmp[2] = '\0'; + if (! sscanf(hextmp, "%x", &schar)) + xtables_error(PARAMETER_PROBLEM, + "Invalid hex char `%c'", s[i]); + info->pattern[sindex] = (char) schar; + if (s[i+2] == ' ') + i += 3; /* spaces included in the hex block */ + else + i += 2; + } else { /* the char is not part of hex data, so just copy */ + info->pattern[sindex] = s[i]; + i++; + } + if (sindex > XT_STRING_MAX_PATTERN_SIZE) + xtables_error(PARAMETER_PROBLEM, "STRING too long \"%s\"", s); + sindex++; + } + info->patlen = sindex; +} + +#define STRING 0x1 +#define ALGO 0x2 +#define FROM 0x4 +#define TO 0x8 +#define ICASE 0x10 + +static int +string_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_string_info *stringinfo = + (struct xt_string_info *)(*match)->data; + const int revision = (*match)->u.user.revision; + + switch (c) { + case '1': + if (*flags & FROM) + xtables_error(PARAMETER_PROBLEM, + "Can't specify multiple --from"); + stringinfo->from_offset = atoi(optarg); + *flags |= FROM; + break; + case '2': + if (*flags & TO) + xtables_error(PARAMETER_PROBLEM, + "Can't specify multiple --to"); + stringinfo->to_offset = atoi(optarg); + *flags |= TO; + break; + case '3': + if (*flags & ALGO) + xtables_error(PARAMETER_PROBLEM, + "Can't specify multiple --algo"); + parse_algo(optarg, stringinfo); + *flags |= ALGO; + break; + case '4': + if (*flags & STRING) + xtables_error(PARAMETER_PROBLEM, + "Can't specify multiple --string"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_string(optarg, stringinfo); + if (invert) { + if (revision == 0) + stringinfo->u.v0.invert = 1; + else + stringinfo->u.v1.flags |= XT_STRING_FLAG_INVERT; + } + *flags |= STRING; + break; + + case '5': + if (*flags & STRING) + xtables_error(PARAMETER_PROBLEM, + "Can't specify multiple --hex-string"); + + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_hex_string(optarg, stringinfo); /* sets length */ + if (invert) { + if (revision == 0) + stringinfo->u.v0.invert = 1; + else + stringinfo->u.v1.flags |= XT_STRING_FLAG_INVERT; + } + *flags |= STRING; + break; + + case '6': + if (revision == 0) + xtables_error(VERSION_PROBLEM, + "Kernel doesn't support --icase"); + + stringinfo->u.v1.flags |= XT_STRING_FLAG_IGNORECASE; + *flags |= ICASE; + break; + + default: + return 0; + } + return 1; +} + +static void string_check(unsigned int flags) +{ + if (!(flags & STRING)) + xtables_error(PARAMETER_PROBLEM, + "STRING match: You must specify `--string' or " + "`--hex-string'"); + if (!(flags & ALGO)) + xtables_error(PARAMETER_PROBLEM, + "STRING match: You must specify `--algo'"); +} + +/* Test to see if the string contains non-printable chars or quotes */ +static unsigned short int +is_hex_string(const char *str, const unsigned short int len) +{ + unsigned int i; + for (i=0; i < len; i++) + if (! isprint(str[i])) + return 1; /* string contains at least one non-printable char */ + /* use hex output if the last char is a "\" */ + if ((unsigned char) str[len-1] == 0x5c) + return 1; + return 0; +} + +/* Print string with "|" chars included as one would pass to --hex-string */ +static void +print_hex_string(const char *str, const unsigned short int len) +{ + unsigned int i; + /* start hex block */ + printf("\"|"); + for (i=0; i < len; i++) { + /* see if we need to prepend a zero */ + if ((unsigned char) str[i] <= 0x0F) + printf("0%x", (unsigned char) str[i]); + else + printf("%x", (unsigned char) str[i]); + } + /* close hex block */ + printf("|\" "); +} + +static void +print_string(const char *str, const unsigned short int len) +{ + unsigned int i; + printf("\""); + for (i=0; i < len; i++) { + if ((unsigned char) str[i] == 0x22) /* escape any embedded quotes */ + printf("%c", 0x5c); + printf("%c", (unsigned char) str[i]); + } + printf("\" "); /* closing space and quote */ +} + +static void +string_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_string_info *info = + (const struct xt_string_info*) match->data; + const int revision = match->u.user.revision; + int invert = (revision == 0 ? info->u.v0.invert : + info->u.v1.flags & XT_STRING_FLAG_INVERT); + + if (is_hex_string(info->pattern, info->patlen)) { + printf("STRING match %s", invert ? "!" : ""); + print_hex_string(info->pattern, info->patlen); + } else { + printf("STRING match %s", invert ? "!" : ""); + print_string(info->pattern, info->patlen); + } + printf("ALGO name %s ", info->algo); + if (info->from_offset != 0) + printf("FROM %u ", info->from_offset); + if (info->to_offset != 0) + printf("TO %u ", info->to_offset); + if (revision > 0 && info->u.v1.flags & XT_STRING_FLAG_IGNORECASE) + printf("ICASE "); +} + +static void string_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_string_info *info = + (const struct xt_string_info*) match->data; + const int revision = match->u.user.revision; + int invert = (revision == 0 ? info->u.v0.invert : + info->u.v1.flags & XT_STRING_FLAG_INVERT); + + if (is_hex_string(info->pattern, info->patlen)) { + printf("%s--hex-string ", (invert) ? "! ": ""); + print_hex_string(info->pattern, info->patlen); + } else { + printf("%s--string ", (invert) ? "! ": ""); + print_string(info->pattern, info->patlen); + } + printf("--algo %s ", info->algo); + if (info->from_offset != 0) + printf("--from %u ", info->from_offset); + if (info->to_offset != 0) + printf("--to %u ", info->to_offset); + if (revision > 0 && info->u.v1.flags & XT_STRING_FLAG_IGNORECASE) + printf("--icase "); +} + + +static struct xtables_match string_mt_reg[] = { + { + .name = "string", + .revision = 0, + .family = NFPROTO_UNSPEC, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_string_info)), + .userspacesize = offsetof(struct xt_string_info, config), + .help = string_help, + .init = string_init, + .parse = string_parse, + .final_check = string_check, + .print = string_print, + .save = string_save, + .extra_opts = string_opts, + }, + { + .name = "string", + .revision = 1, + .family = NFPROTO_UNSPEC, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_string_info)), + .userspacesize = offsetof(struct xt_string_info, config), + .help = string_help, + .init = string_init, + .parse = string_parse, + .final_check = string_check, + .print = string_print, + .save = string_save, + .extra_opts = string_opts, + }, +}; + +void _init(void) +{ + xtables_register_matches(string_mt_reg, ARRAY_SIZE(string_mt_reg)); +} diff --git a/extensions/libxt_string.man b/extensions/libxt_string.man new file mode 100644 index 0000000..b6b271d --- /dev/null +++ b/extensions/libxt_string.man @@ -0,0 +1,18 @@ +This modules matches a given string by using some pattern matching strategy. It requires a linux kernel >= 2.6.14. +.TP +\fB\-\-algo\fP {\fBbm\fP|\fBkmp\fP} +Select the pattern matching strategy. (bm = Boyer-Moore, kmp = Knuth-Pratt-Morris) +.TP +\fB\-\-from\fP \fIoffset\fP +Set the offset from which it starts looking for any matching. If not passed, default is 0. +.TP +\fB\-\-to\fP \fIoffset\fP +Set the offset up to which should be scanned. That is, byte \fIoffset\fP-1 +(counting from 0) is the last one that is scanned. +If not passed, default is the packet size. +.TP +[\fB!\fP] \fB\-\-string\fP \fIpattern\fP +Matches the given pattern. +.TP +[\fB!\fP] \fB\-\-hex\-string\fP \fIpattern\fP +Matches the given pattern in hex notation. diff --git a/extensions/libxt_tcp.c b/extensions/libxt_tcp.c new file mode 100644 index 0000000..75551d7 --- /dev/null +++ b/extensions/libxt_tcp.c @@ -0,0 +1,395 @@ +/* Shared library add-on to iptables to add TCP support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <netinet/in.h> +#include <xtables.h> +#include <linux/netfilter/xt_tcpudp.h> + +static void tcp_help(void) +{ + printf( +"tcp match options:\n" +"[!] --tcp-flags mask comp match when TCP flags & mask == comp\n" +" (Flags: SYN ACK FIN RST URG PSH ALL NONE)\n" +"[!] --syn match when only SYN flag set\n" +" (equivalent to --tcp-flags SYN,RST,ACK,FIN SYN)\n" +"[!] --source-port port[:port]\n" +" --sport ...\n" +" match source port(s)\n" +"[!] --destination-port port[:port]\n" +" --dport ...\n" +" match destination port(s)\n" +"[!] --tcp-option number match if TCP option set\n"); +} + +static const struct option tcp_opts[] = { + { "source-port", 1, NULL, '1' }, + { "sport", 1, NULL, '1' }, /* synonym */ + { "destination-port", 1, NULL, '2' }, + { "dport", 1, NULL, '2' }, /* synonym */ + { "syn", 0, NULL, '3' }, + { "tcp-flags", 1, NULL, '4' }, + { "tcp-option", 1, NULL, '5' }, + { .name = NULL } +}; + +static void +parse_tcp_ports(const char *portstring, u_int16_t *ports) +{ + char *buffer; + char *cp; + + buffer = strdup(portstring); + if ((cp = strchr(buffer, ':')) == NULL) + ports[0] = ports[1] = xtables_parse_port(buffer, "tcp"); + else { + *cp = '\0'; + cp++; + + ports[0] = buffer[0] ? xtables_parse_port(buffer, "tcp") : 0; + ports[1] = cp[0] ? xtables_parse_port(cp, "tcp") : 0xFFFF; + + if (ports[0] > ports[1]) + xtables_error(PARAMETER_PROBLEM, + "invalid portrange (min > max)"); + } + free(buffer); +} + +struct tcp_flag_names { + const char *name; + unsigned int flag; +}; + +static const struct tcp_flag_names tcp_flag_names[] += { { "FIN", 0x01 }, + { "SYN", 0x02 }, + { "RST", 0x04 }, + { "PSH", 0x08 }, + { "ACK", 0x10 }, + { "URG", 0x20 }, + { "ALL", 0x3F }, + { "NONE", 0 }, +}; + +static unsigned int +parse_tcp_flag(const char *flags) +{ + unsigned int ret = 0; + char *ptr; + char *buffer; + + buffer = strdup(flags); + + for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) { + unsigned int i; + for (i = 0; i < ARRAY_SIZE(tcp_flag_names); ++i) + if (strcasecmp(tcp_flag_names[i].name, ptr) == 0) { + ret |= tcp_flag_names[i].flag; + break; + } + if (i == ARRAY_SIZE(tcp_flag_names)) + xtables_error(PARAMETER_PROBLEM, + "Unknown TCP flag `%s'", ptr); + } + + free(buffer); + return ret; +} + +static void +parse_tcp_flags(struct xt_tcp *tcpinfo, + const char *mask, + const char *cmp, + int invert) +{ + tcpinfo->flg_mask = parse_tcp_flag(mask); + tcpinfo->flg_cmp = parse_tcp_flag(cmp); + + if (invert) + tcpinfo->invflags |= XT_TCP_INV_FLAGS; +} + +static void +parse_tcp_option(const char *option, u_int8_t *result) +{ + unsigned int ret; + + if (!xtables_strtoui(option, NULL, &ret, 1, UINT8_MAX)) + xtables_error(PARAMETER_PROBLEM, "Bad TCP option \"%s\"", option); + + *result = ret; +} + +static void tcp_init(struct xt_entry_match *m) +{ + struct xt_tcp *tcpinfo = (struct xt_tcp *)m->data; + + tcpinfo->spts[1] = tcpinfo->dpts[1] = 0xFFFF; +} + +#define TCP_SRC_PORTS 0x01 +#define TCP_DST_PORTS 0x02 +#define TCP_FLAGS 0x04 +#define TCP_OPTION 0x08 + +static int +tcp_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_tcp *tcpinfo = (struct xt_tcp *)(*match)->data; + + switch (c) { + case '1': + if (*flags & TCP_SRC_PORTS) + xtables_error(PARAMETER_PROBLEM, + "Only one `--source-port' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_tcp_ports(optarg, tcpinfo->spts); + if (invert) + tcpinfo->invflags |= XT_TCP_INV_SRCPT; + *flags |= TCP_SRC_PORTS; + break; + + case '2': + if (*flags & TCP_DST_PORTS) + xtables_error(PARAMETER_PROBLEM, + "Only one `--destination-port' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_tcp_ports(optarg, tcpinfo->dpts); + if (invert) + tcpinfo->invflags |= XT_TCP_INV_DSTPT; + *flags |= TCP_DST_PORTS; + break; + + case '3': + if (*flags & TCP_FLAGS) + xtables_error(PARAMETER_PROBLEM, + "Only one of `--syn' or `--tcp-flags' " + " allowed"); + parse_tcp_flags(tcpinfo, "SYN,RST,ACK,FIN", "SYN", invert); + *flags |= TCP_FLAGS; + break; + + case '4': + if (*flags & TCP_FLAGS) + xtables_error(PARAMETER_PROBLEM, + "Only one of `--syn' or `--tcp-flags' " + " allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + + if (!argv[optind] + || argv[optind][0] == '-' || argv[optind][0] == '!') + xtables_error(PARAMETER_PROBLEM, + "--tcp-flags requires two args."); + + parse_tcp_flags(tcpinfo, optarg, argv[optind], + invert); + optind++; + *flags |= TCP_FLAGS; + break; + + case '5': + if (*flags & TCP_OPTION) + xtables_error(PARAMETER_PROBLEM, + "Only one `--tcp-option' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_tcp_option(optarg, &tcpinfo->option); + if (invert) + tcpinfo->invflags |= XT_TCP_INV_OPTION; + *flags |= TCP_OPTION; + break; + + default: + return 0; + } + + return 1; +} + +static char * +port_to_service(int port) +{ + struct servent *service; + + if ((service = getservbyport(htons(port), "tcp"))) + return service->s_name; + + return NULL; +} + +static void +print_port(u_int16_t port, int numeric) +{ + char *service; + + if (numeric || (service = port_to_service(port)) == NULL) + printf("%u", port); + else + printf("%s", service); +} + +static void +print_ports(const char *name, u_int16_t min, u_int16_t max, + int invert, int numeric) +{ + const char *inv = invert ? "!" : ""; + + if (min != 0 || max != 0xFFFF || invert) { + printf("%s", name); + if (min == max) { + printf(":%s", inv); + print_port(min, numeric); + } else { + printf("s:%s", inv); + print_port(min, numeric); + printf(":"); + print_port(max, numeric); + } + printf(" "); + } +} + +static void +print_option(u_int8_t option, int invert, int numeric) +{ + if (option || invert) + printf("option=%s%u ", invert ? "!" : "", option); +} + +static void +print_tcpf(u_int8_t flags) +{ + int have_flag = 0; + + while (flags) { + unsigned int i; + + for (i = 0; (flags & tcp_flag_names[i].flag) == 0; i++); + + if (have_flag) + printf(","); + printf("%s", tcp_flag_names[i].name); + have_flag = 1; + + flags &= ~tcp_flag_names[i].flag; + } + + if (!have_flag) + printf("NONE"); +} + +static void +print_flags(u_int8_t mask, u_int8_t cmp, int invert, int numeric) +{ + if (mask || invert) { + printf("flags:%s", invert ? "!" : ""); + if (numeric) + printf("0x%02X/0x%02X ", mask, cmp); + else { + print_tcpf(mask); + printf("/"); + print_tcpf(cmp); + printf(" "); + } + } +} + +static void +tcp_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_tcp *tcp = (struct xt_tcp *)match->data; + + printf("tcp "); + print_ports("spt", tcp->spts[0], tcp->spts[1], + tcp->invflags & XT_TCP_INV_SRCPT, + numeric); + print_ports("dpt", tcp->dpts[0], tcp->dpts[1], + tcp->invflags & XT_TCP_INV_DSTPT, + numeric); + print_option(tcp->option, + tcp->invflags & XT_TCP_INV_OPTION, + numeric); + print_flags(tcp->flg_mask, tcp->flg_cmp, + tcp->invflags & XT_TCP_INV_FLAGS, + numeric); + if (tcp->invflags & ~XT_TCP_INV_MASK) + printf("Unknown invflags: 0x%X ", + tcp->invflags & ~XT_TCP_INV_MASK); +} + +static void tcp_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_tcp *tcpinfo = (struct xt_tcp *)match->data; + + if (tcpinfo->spts[0] != 0 + || tcpinfo->spts[1] != 0xFFFF) { + if (tcpinfo->invflags & XT_TCP_INV_SRCPT) + printf("! "); + if (tcpinfo->spts[0] + != tcpinfo->spts[1]) + printf("--sport %u:%u ", + tcpinfo->spts[0], + tcpinfo->spts[1]); + else + printf("--sport %u ", + tcpinfo->spts[0]); + } + + if (tcpinfo->dpts[0] != 0 + || tcpinfo->dpts[1] != 0xFFFF) { + if (tcpinfo->invflags & XT_TCP_INV_DSTPT) + printf("! "); + if (tcpinfo->dpts[0] + != tcpinfo->dpts[1]) + printf("--dport %u:%u ", + tcpinfo->dpts[0], + tcpinfo->dpts[1]); + else + printf("--dport %u ", + tcpinfo->dpts[0]); + } + + if (tcpinfo->option + || (tcpinfo->invflags & XT_TCP_INV_OPTION)) { + if (tcpinfo->invflags & XT_TCP_INV_OPTION) + printf("! "); + printf("--tcp-option %u ", tcpinfo->option); + } + + if (tcpinfo->flg_mask + || (tcpinfo->invflags & XT_TCP_INV_FLAGS)) { + if (tcpinfo->invflags & XT_TCP_INV_FLAGS) + printf("! "); + printf("--tcp-flags "); + if (tcpinfo->flg_mask != 0xFF) { + print_tcpf(tcpinfo->flg_mask); + } + printf(" "); + print_tcpf(tcpinfo->flg_cmp); + printf(" "); + } +} + +static struct xtables_match tcp_match = { + .family = NFPROTO_UNSPEC, + .name = "tcp", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_tcp)), + .userspacesize = XT_ALIGN(sizeof(struct xt_tcp)), + .help = tcp_help, + .init = tcp_init, + .parse = tcp_parse, + .print = tcp_print, + .save = tcp_save, + .extra_opts = tcp_opts, +}; + +void +_init(void) +{ + xtables_register_match(&tcp_match); +} diff --git a/extensions/libxt_tcp.man b/extensions/libxt_tcp.man new file mode 100644 index 0000000..7a16118 --- /dev/null +++ b/extensions/libxt_tcp.man @@ -0,0 +1,44 @@ +These extensions can be used if `\-\-protocol tcp' is specified. It +provides the following options: +.TP +[\fB!\fP] \fB\-\-source\-port\fP,\fB\-\-sport\fP \fIport\fP[\fB:\fP\fIport\fP] +Source port or port range specification. This can either be a service +name or a port number. An inclusive range can also be specified, +using the format \fIfirst\fP\fB:\fP\fIlast\fP. +If the first port is omitted, "0" is assumed; if the last is omitted, +"65535" is assumed. +If the first port is greater than the second one they will be swapped. +The flag +\fB\-\-sport\fP +is a convenient alias for this option. +.TP +[\fB!\fP] \fB\-\-destination\-port\fP,\fB\-\-dport\fP \fIport\fP[\fB:\fP\fIport\fP] +Destination port or port range specification. The flag +\fB\-\-dport\fP +is a convenient alias for this option. +.TP +[\fB!\fP] \fB\-\-tcp\-flags\fP \fImask\fP \fIcomp\fP +Match when the TCP flags are as specified. The first argument \fImask\fP is the +flags which we should examine, written as a comma-separated list, and +the second argument \fIcomp\fP is a comma-separated list of flags which must be +set. Flags are: +.BR "SYN ACK FIN RST URG PSH ALL NONE" . +Hence the command +.nf + iptables \-A FORWARD \-p tcp \-\-tcp\-flags SYN,ACK,FIN,RST SYN +.fi +will only match packets with the SYN flag set, and the ACK, FIN and +RST flags unset. +.TP +[\fB!\fP] \fB\-\-syn\fP +Only match TCP packets with the SYN bit set and the ACK,RST and FIN bits +cleared. Such packets are used to request TCP connection initiation; +for example, blocking such packets coming in an interface will prevent +incoming TCP connections, but outgoing TCP connections will be +unaffected. +It is equivalent to \fB\-\-tcp\-flags SYN,RST,ACK,FIN SYN\fP. +If the "!" flag precedes the "\-\-syn", the sense of the +option is inverted. +.TP +[\fB!\fP] \fB\-\-tcp\-option\fP \fInumber\fP +Match if TCP option set. diff --git a/extensions/libxt_tcpmss.c b/extensions/libxt_tcpmss.c new file mode 100644 index 0000000..b54a890 --- /dev/null +++ b/extensions/libxt_tcpmss.c @@ -0,0 +1,128 @@ +/* Shared library add-on to iptables to add tcp MSS matching support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include <xtables.h> +#include <linux/netfilter/xt_tcpmss.h> + +static void tcpmss_help(void) +{ + printf( +"tcpmss match options:\n" +"[!] --mss value[:value] Match TCP MSS range.\n" +" (only valid for TCP SYN or SYN/ACK packets)\n"); +} + +static const struct option tcpmss_opts[] = { + { "mss", 1, NULL, '1' }, + { .name = NULL } +}; + +static u_int16_t +parse_tcp_mssvalue(const char *mssvalue) +{ + unsigned int mssvaluenum; + + if (xtables_strtoui(mssvalue, NULL, &mssvaluenum, 0, UINT16_MAX)) + return mssvaluenum; + + xtables_error(PARAMETER_PROBLEM, + "Invalid mss `%s' specified", mssvalue); +} + +static void +parse_tcp_mssvalues(const char *mssvaluestring, + u_int16_t *mss_min, u_int16_t *mss_max) +{ + char *buffer; + char *cp; + + buffer = strdup(mssvaluestring); + if ((cp = strchr(buffer, ':')) == NULL) + *mss_min = *mss_max = parse_tcp_mssvalue(buffer); + else { + *cp = '\0'; + cp++; + + *mss_min = buffer[0] ? parse_tcp_mssvalue(buffer) : 0; + *mss_max = cp[0] ? parse_tcp_mssvalue(cp) : 0xFFFF; + } + free(buffer); +} + +static int +tcpmss_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_tcpmss_match_info *mssinfo = + (struct xt_tcpmss_match_info *)(*match)->data; + + switch (c) { + case '1': + if (*flags) + xtables_error(PARAMETER_PROBLEM, + "Only one `--mss' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_tcp_mssvalues(optarg, + &mssinfo->mss_min, &mssinfo->mss_max); + if (invert) + mssinfo->invert = 1; + *flags = 1; + break; + default: + return 0; + } + return 1; +} + +static void tcpmss_check(unsigned int flags) +{ + if (!flags) + xtables_error(PARAMETER_PROBLEM, + "tcpmss match: You must specify `--mss'"); +} + +static void +tcpmss_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_tcpmss_match_info *info = (void *)match->data; + + printf("tcpmss match %s", info->invert ? "!" : ""); + if (info->mss_min == info->mss_max) + printf("%u ", info->mss_min); + else + printf("%u:%u ", info->mss_min, info->mss_max); +} + +static void tcpmss_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_tcpmss_match_info *info = (void *)match->data; + + printf("%s--mss ", info->invert ? "! " : ""); + if (info->mss_min == info->mss_max) + printf("%u ", info->mss_min); + else + printf("%u:%u ", info->mss_min, info->mss_max); +} + +static struct xtables_match tcpmss_match = { + .family = NFPROTO_UNSPEC, + .name = "tcpmss", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_tcpmss_match_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_tcpmss_match_info)), + .help = tcpmss_help, + .parse = tcpmss_parse, + .final_check = tcpmss_check, + .print = tcpmss_print, + .save = tcpmss_save, + .extra_opts = tcpmss_opts, +}; + +void _init(void) +{ + xtables_register_match(&tcpmss_match); +} diff --git a/extensions/libxt_tcpmss.man b/extensions/libxt_tcpmss.man new file mode 100644 index 0000000..8ee715c --- /dev/null +++ b/extensions/libxt_tcpmss.man @@ -0,0 +1,4 @@ +This matches the TCP MSS (maximum segment size) field of the TCP header. You can only use this on TCP SYN or SYN/ACK packets, since the MSS is only negotiated during the TCP handshake at connection startup time. +.TP +[\fB!\fP] \fB\-\-mss\fP \fIvalue\fP[\fB:\fP\fIvalue\fP] +Match a given TCP MSS value or range. diff --git a/extensions/libxt_time.c b/extensions/libxt_time.c new file mode 100644 index 0000000..098fc9c --- /dev/null +++ b/extensions/libxt_time.c @@ -0,0 +1,485 @@ +/* + * libxt_time - iptables part for xt_time + * Copyright © CC Computer Consultants GmbH, 2007 + * Contact: <jengelh@computergmbh.de> + * + * libxt_time.c 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 or 3 of the License. + * + * Based on libipt_time.c. + */ +#include <sys/types.h> +#include <getopt.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <time.h> +#include <limits.h> + +#include <linux/netfilter/xt_time.h> +#include <xtables.h> + +enum { /* getopt "seen" bits */ + F_DATE_START = 1 << 0, + F_DATE_STOP = 1 << 1, + F_TIME_START = 1 << 2, + F_TIME_STOP = 1 << 3, + F_MONTHDAYS = 1 << 4, + F_WEEKDAYS = 1 << 5, + F_TIMEZONE = 1 << 6, +}; + +static const char *const week_days[] = { + NULL, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", +}; + +static const struct option time_opts[] = { + {"datestart", true, NULL, 'D'}, + {"datestop", true, NULL, 'E'}, + {"timestart", true, NULL, 'X'}, + {"timestop", true, NULL, 'Y'}, + {"weekdays", true, NULL, 'w'}, + {"monthdays", true, NULL, 'm'}, + {"localtz", false, NULL, 'l'}, + {"utc", false, NULL, 'u'}, + { .name = NULL } +}; + +static void time_help(void) +{ + printf( +"time match options:\n" +" --datestart time Start and stop time, to be given in ISO 8601\n" +" --datestop time (YYYY[-MM[-DD[Thh[:mm[:ss]]]]])\n" +" --timestart time Start and stop daytime (hh:mm[:ss])\n" +" --timestop time (between 00:00:00 and 23:59:59)\n" +"[!] --monthdays value List of days on which to match, separated by comma\n" +" (Possible days: 1 to 31; defaults to all)\n" +"[!] --weekdays value List of weekdays on which to match, sep. by comma\n" +" (Possible days: Mon,Tue,Wed,Thu,Fri,Sat,Sun or 1 to 7\n" +" Defaults to all weekdays.)\n" +" --localtz/--utc Time is interpreted as UTC/local time\n"); +} + +static void time_init(struct xt_entry_match *m) +{ + struct xt_time_info *info = (void *)m->data; + + /* By default, we match on every day, every daytime */ + info->monthdays_match = XT_TIME_ALL_MONTHDAYS; + info->weekdays_match = XT_TIME_ALL_WEEKDAYS; + info->daytime_start = XT_TIME_MIN_DAYTIME; + info->daytime_stop = XT_TIME_MAX_DAYTIME; + + /* ...and have no date-begin or date-end boundary */ + info->date_start = 0; + info->date_stop = INT_MAX; + + /* local time is default */ + info->flags |= XT_TIME_LOCAL_TZ; +} + +static time_t time_parse_date(const char *s, bool end) +{ + unsigned int month = 1, day = 1, hour = 0, minute = 0, second = 0; + unsigned int year = end ? 2038 : 1970; + const char *os = s; + struct tm tm; + time_t ret; + char *e; + + year = strtoul(s, &e, 10); + if ((*e != '-' && *e != '\0') || year < 1970 || year > 2038) + goto out; + if (*e == '\0') + goto eval; + + s = e + 1; + month = strtoul(s, &e, 10); + if ((*e != '-' && *e != '\0') || month > 12) + goto out; + if (*e == '\0') + goto eval; + + s = e + 1; + day = strtoul(s, &e, 10); + if ((*e != 'T' && *e != '\0') || day > 31) + goto out; + if (*e == '\0') + goto eval; + + s = e + 1; + hour = strtoul(s, &e, 10); + if ((*e != ':' && *e != '\0') || hour > 23) + goto out; + if (*e == '\0') + goto eval; + + s = e + 1; + minute = strtoul(s, &e, 10); + if ((*e != ':' && *e != '\0') || minute > 59) + goto out; + if (*e == '\0') + goto eval; + + s = e + 1; + second = strtoul(s, &e, 10); + if (*e != '\0' || second > 59) + goto out; + + eval: + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = minute; + tm.tm_sec = second; + ret = mktime(&tm); + if (ret >= 0) + return ret; + perror("mktime"); + xtables_error(OTHER_PROBLEM, "mktime returned an error"); + + out: + xtables_error(PARAMETER_PROBLEM, "Invalid date \"%s\" specified. Should " + "be YYYY[-MM[-DD[Thh[:mm[:ss]]]]]", os); + return -1; +} + +static unsigned int time_parse_minutes(const char *s) +{ + unsigned int hour, minute, second = 0; + char *e; + + hour = strtoul(s, &e, 10); + if (*e != ':' || hour > 23) + goto out; + + s = e + 1; + minute = strtoul(s, &e, 10); + if ((*e != ':' && *e != '\0') || minute > 59) + goto out; + if (*e == '\0') + goto eval; + + s = e + 1; + second = strtoul(s, &e, 10); + if (*e != '\0' || second > 59) + goto out; + + eval: + return 60 * 60 * hour + 60 * minute + second; + + out: + xtables_error(PARAMETER_PROBLEM, "invalid time \"%s\" specified, " + "should be hh:mm[:ss] format and within the boundaries", s); + return -1; +} + +static const char *my_strseg(char *buf, unsigned int buflen, + const char **arg, char delim) +{ + const char *sep; + + if (*arg == NULL || **arg == '\0') + return NULL; + sep = strchr(*arg, delim); + if (sep == NULL) { + snprintf(buf, buflen, "%s", *arg); + *arg = NULL; + return buf; + } + snprintf(buf, buflen, "%.*s", (unsigned int)(sep - *arg), *arg); + *arg = sep + 1; + return buf; +} + +static uint32_t time_parse_monthdays(const char *arg) +{ + char day[3], *err = NULL; + uint32_t ret = 0; + unsigned int i; + + while (my_strseg(day, sizeof(day), &arg, ',') != NULL) { + i = strtoul(day, &err, 0); + if ((*err != ',' && *err != '\0') || i > 31) + xtables_error(PARAMETER_PROBLEM, + "%s is not a valid day for --monthdays", day); + ret |= 1 << i; + } + + return ret; +} + +static unsigned int time_parse_weekdays(const char *arg) +{ + char day[4], *err = NULL; + unsigned int i, ret = 0; + bool valid; + + while (my_strseg(day, sizeof(day), &arg, ',') != NULL) { + i = strtoul(day, &err, 0); + if (*err == '\0') { + if (i == 0) + xtables_error(PARAMETER_PROBLEM, + "No, the week does NOT begin with Sunday."); + ret |= 1 << i; + continue; + } + + valid = false; + for (i = 1; i < ARRAY_SIZE(week_days); ++i) + if (strncmp(day, week_days[i], 2) == 0) { + ret |= 1 << i; + valid = true; + } + + if (!valid) + xtables_error(PARAMETER_PROBLEM, + "%s is not a valid day specifier", day); + } + + return ret; +} + +static int time_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_time_info *info = (void *)(*match)->data; + + switch (c) { + case 'D': /* --datestart */ + if (*flags & F_DATE_START) + xtables_error(PARAMETER_PROBLEM, + "Cannot specify --datestart twice"); + if (invert) + xtables_error(PARAMETER_PROBLEM, + "Unexpected \"!\" with --datestart"); + info->date_start = time_parse_date(optarg, false); + *flags |= F_DATE_START; + return 1; + case 'E': /* --datestop */ + if (*flags & F_DATE_STOP) + xtables_error(PARAMETER_PROBLEM, + "Cannot specify --datestop more than once"); + if (invert) + xtables_error(PARAMETER_PROBLEM, + "unexpected \"!\" with --datestop"); + info->date_stop = time_parse_date(optarg, true); + *flags |= F_DATE_STOP; + return 1; + case 'X': /* --timestart */ + if (*flags & F_TIME_START) + xtables_error(PARAMETER_PROBLEM, + "Cannot specify --timestart more than once"); + if (invert) + xtables_error(PARAMETER_PROBLEM, + "Unexpected \"!\" with --timestart"); + info->daytime_start = time_parse_minutes(optarg); + *flags |= F_TIME_START; + return 1; + case 'Y': /* --timestop */ + if (*flags & F_TIME_STOP) + xtables_error(PARAMETER_PROBLEM, + "Cannot specify --timestop more than once"); + if (invert) + xtables_error(PARAMETER_PROBLEM, + "Unexpected \"!\" with --timestop"); + info->daytime_stop = time_parse_minutes(optarg); + *flags |= F_TIME_STOP; + return 1; + case 'l': /* --localtz */ + if (*flags & F_TIMEZONE) + xtables_error(PARAMETER_PROBLEM, + "Can only specify exactly one of --localtz or --utc"); + info->flags |= XT_TIME_LOCAL_TZ; + *flags |= F_TIMEZONE; + return 1; + case 'm': /* --monthdays */ + if (*flags & F_MONTHDAYS) + xtables_error(PARAMETER_PROBLEM, + "Cannot specify --monthdays more than once"); + info->monthdays_match = time_parse_monthdays(optarg); + if (invert) + info->monthdays_match ^= XT_TIME_ALL_MONTHDAYS; + *flags |= F_MONTHDAYS; + return 1; + case 'w': /* --weekdays */ + if (*flags & F_WEEKDAYS) + xtables_error(PARAMETER_PROBLEM, + "Cannot specify --weekdays more than once"); + info->weekdays_match = time_parse_weekdays(optarg); + if (invert) + info->weekdays_match ^= XT_TIME_ALL_WEEKDAYS; + *flags |= F_WEEKDAYS; + return 1; + case 'u': /* --utc */ + if (*flags & F_TIMEZONE) + xtables_error(PARAMETER_PROBLEM, + "Can only specify exactly one of --localtz or --utc"); + info->flags &= ~XT_TIME_LOCAL_TZ; + *flags |= F_TIMEZONE; + return 1; + } + return 0; +} + +static void time_print_date(time_t date, const char *command) +{ + struct tm *t; + + /* If it is the default value, do not print it. */ + if (date == 0 || date == LONG_MAX) + return; + + t = localtime(&date); + if (command != NULL) + /* + * Need a contiguous string (no whitespaces), hence using + * the ISO 8601 "T" variant. + */ + printf("%s %04u-%02u-%02uT%02u:%02u:%02u ", + command, t->tm_year + 1900, t->tm_mon + 1, + t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); + else + printf("%04u-%02u-%02u %02u:%02u:%02u ", + t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); +} + +static void time_print_monthdays(uint32_t mask, bool human_readable) +{ + unsigned int i, nbdays = 0; + + for (i = 1; i <= 31; ++i) + if (mask & (1 << i)) { + if (nbdays++ > 0) + printf(","); + printf("%u", i); + if (human_readable) + switch (i % 10) { + case 1: + printf("st"); + break; + case 2: + printf("nd"); + break; + case 3: + printf("rd"); + break; + default: + printf("th"); + break; + } + } + printf(" "); +} + +static void time_print_weekdays(unsigned int mask) +{ + unsigned int i, nbdays = 0; + + for (i = 1; i <= 7; ++i) + if (mask & (1 << i)) { + if (nbdays > 0) + printf(",%s", week_days[i]); + else + printf("%s", week_days[i]); + ++nbdays; + } + printf(" "); +} + +static inline void divide_time(unsigned int fulltime, unsigned int *hours, + unsigned int *minutes, unsigned int *seconds) +{ + *seconds = fulltime % 60; + fulltime /= 60; + *minutes = fulltime % 60; + *hours = fulltime / 60; +} + +static void time_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_time_info *info = (const void *)match->data; + unsigned int h, m, s; + + printf("TIME "); + + if (info->daytime_start != XT_TIME_MIN_DAYTIME || + info->daytime_stop != XT_TIME_MAX_DAYTIME) { + divide_time(info->daytime_start, &h, &m, &s); + printf("from %02u:%02u:%02u ", h, m, s); + divide_time(info->daytime_stop, &h, &m, &s); + printf("to %02u:%02u:%02u ", h, m, s); + } + if (info->weekdays_match != XT_TIME_ALL_WEEKDAYS) { + printf("on "); + time_print_weekdays(info->weekdays_match); + } + if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) { + printf("on "); + time_print_monthdays(info->monthdays_match, true); + } + if (info->date_start != 0) { + printf("starting from "); + time_print_date(info->date_start, NULL); + } + if (info->date_stop != INT_MAX) { + printf("until date "); + time_print_date(info->date_stop, NULL); + } + if (!(info->flags & XT_TIME_LOCAL_TZ)) + printf("UTC "); +} + +static void time_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_time_info *info = (const void *)match->data; + unsigned int h, m, s; + + if (info->daytime_start != XT_TIME_MIN_DAYTIME || + info->daytime_stop != XT_TIME_MAX_DAYTIME) { + divide_time(info->daytime_start, &h, &m, &s); + printf("--timestart %02u:%02u:%02u ", h, m, s); + divide_time(info->daytime_stop, &h, &m, &s); + printf("--timestop %02u:%02u:%02u ", h, m, s); + } + if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) { + printf("--monthdays "); + time_print_monthdays(info->monthdays_match, false); + } + if (info->weekdays_match != XT_TIME_ALL_WEEKDAYS) { + printf("--weekdays "); + time_print_weekdays(info->weekdays_match); + printf(" "); + } + time_print_date(info->date_start, "--datestart"); + time_print_date(info->date_stop, "--datestop"); + if (!(info->flags & XT_TIME_LOCAL_TZ)) + printf("--utc "); +} + +static struct xtables_match time_match = { + .name = "time", + .family = NFPROTO_UNSPEC, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_time_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_time_info)), + .help = time_help, + .init = time_init, + .parse = time_parse, + .print = time_print, + .save = time_save, + .extra_opts = time_opts, +}; + +void _init(void) +{ + xtables_register_match(&time_match); +} diff --git a/extensions/libxt_time.man b/extensions/libxt_time.man new file mode 100644 index 0000000..83625a2 --- /dev/null +++ b/extensions/libxt_time.man @@ -0,0 +1,69 @@ +This matches if the packet arrival time/date is within a given range. All +options are optional, but are ANDed when specified. +.TP +\fB\-\-datestart\fP \fIYYYY\fP[\fB\-\fP\fIMM\fP[\fB\-\fP\fIDD\fP[\fBT\fP\fIhh\fP[\fB:\fP\fImm\fP[\fB:\fP\fIss\fP]]]]] +.TP +\fB\-\-datestop\fP \fIYYYY\fP[\fB\-\fP\fIMM\fP[\fB\-\fP\fIDD\fP[\fBT\fP\fIhh\fP[\fB:\fP\fImm\fP[\fB:\fP\fIss\fP]]]]] +.IP +Only match during the given time, which must be in ISO 8601 "T" notation. +The possible time range is 1970-01-01T00:00:00 to 2038-01-19T04:17:07. +.IP +If \-\-datestart or \-\-datestop are not specified, it will default to 1970-01-01 +and 2038-01-19, respectively. +.TP +\fB\-\-timestart\fP \fIhh\fP\fB:\fP\fImm\fP[\fB:\fP\fIss\fP] +.TP +\fB\-\-timestop\fP \fIhh\fP\fB:\fP\fImm\fP[\fB:\fP\fIss\fP] +.IP +Only match during the given daytime. The possible time range is 00:00:00 to +23:59:59. Leading zeroes are allowed (e.g. "06:03") and correctly interpreted +as base-10. +.TP +[\fB!\fR] \fB\-\-monthdays\fP \fIday\fP[\fB,\fP\fIday\fP...] +.IP +Only match on the given days of the month. Possible values are \fB1\fR +to \fB31\fR. Note that specifying \fB31\fR will of course not match +on months which do not have a 31st day; the same goes for 28- or 29-day +February. +.TP +[\fB!\fR] \fB\-\-weekdays\fP \fIday\fP[\fB,\fP\fIday\fP...] +.IP +Only match on the given weekdays. Possible values are \fBMon\fR, \fBTue\fR, +\fBWed\fR, \fBThu\fR, \fBFri\fR, \fBSat\fR, \fBSun\fR, or values from \fB1\fR +to \fB7\fR, respectively. You may also use two-character variants (\fBMo\fP, +\fBTu\fR, etc.). +.TP +\fB\-\-utc\fP +.IP +Interpret the times given for \fB\-\-datestart\fP, \fB\-\-datestop\fP, +\fB\-\-timestart\fP and \fB\-\-timestop\fP to be UTC. +.TP +\fB\-\-localtz\fP +.IP +Interpret the times given for \fB\-\-datestart\fP, \fB\-\-datestop\fP, +\fB\-\-timestart\fP and \fB\-\-timestop\fP to be local kernel time. (Default) +.PP +EXAMPLES. To match on weekends, use: +.IP +\-m time \-\-weekdays Sa,Su +.PP +Or, to match (once) on a national holiday block: +.IP +\-m time \-\-datestart 2007\-12\-24 \-\-datestop 2007\-12\-27 +.PP +Since the stop time is actually inclusive, you would need the following stop +time to not match the first second of the new day: +.IP +\-m time \-\-datestart 2007\-01\-01T17:00 \-\-datestop 2007\-01\-01T23:59:59 +.PP +During lunch hour: +.IP +\-m time \-\-timestart 12:30 \-\-timestop 13:30 +.PP +The fourth Friday in the month: +.IP +\-m time \-\-weekdays Fr \-\-monthdays 22,23,24,25,26,27,28 +.PP +(Note that this exploits a certain mathematical property. It is not possible to +say "fourth Thursday OR fourth Friday" in one rule. It is possible with +multiple rules, though.) diff --git a/extensions/libxt_tos.c b/extensions/libxt_tos.c new file mode 100644 index 0000000..6b8cd89 --- /dev/null +++ b/extensions/libxt_tos.c @@ -0,0 +1,178 @@ +/* + * Shared library add-on to iptables to add tos match support + * + * Copyright © CC Computer Consultants GmbH, 2007 + * Contact: Jan Engelhardt <jengelh@computergmbh.de> + */ +#include <getopt.h> +#include <netdb.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <xtables.h> +#include <linux/netfilter/xt_dscp.h> +#include "tos_values.c" + +struct ipt_tos_info { + u_int8_t tos; + u_int8_t invert; +}; + +enum { + FLAG_TOS = 1 << 0, +}; + +static const struct option tos_mt_opts[] = { + {.name = "tos", .has_arg = true, .val = 't'}, + { .name = NULL } +}; + +static void tos_mt_help(void) +{ + const struct tos_symbol_info *symbol; + + printf( +"tos match options:\n" +"[!] --tos value[/mask] Match Type of Service/Priority field value\n" +"[!] --tos symbol Match TOS field (IPv4 only) by symbol\n" +" Accepted symbolic names for value are:\n"); + + for (symbol = tos_symbol_names; symbol->name != NULL; ++symbol) + printf(" (0x%02x) %2u %s\n", + symbol->value, symbol->value, symbol->name); + + printf("\n"); +} + +static int tos_mt_parse_v0(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct ipt_tos_info *info = (void *)(*match)->data; + struct tos_value_mask tvm; + + switch (c) { + case 't': + xtables_param_act(XTF_ONLY_ONCE, "tos", "--tos", *flags & FLAG_TOS); + if (!tos_parse_symbolic(optarg, &tvm, 0xFF)) + xtables_param_act(XTF_BAD_VALUE, "tos", "--tos", optarg); + if (tvm.mask != 0xFF) + xtables_error(PARAMETER_PROBLEM, "tos: Your kernel is " + "too old to support anything besides /0xFF " + "as a mask."); + info->tos = tvm.value; + if (invert) + info->invert = true; + *flags |= FLAG_TOS; + return true; + } + return false; +} + +static int tos_mt_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_tos_match_info *info = (void *)(*match)->data; + struct tos_value_mask tvm = {.mask = 0xFF}; + + switch (c) { + case 't': + xtables_param_act(XTF_ONLY_ONCE, "tos", "--tos", *flags & FLAG_TOS); + if (!tos_parse_symbolic(optarg, &tvm, 0x3F)) + xtables_param_act(XTF_BAD_VALUE, "tos", "--tos", optarg); + info->tos_value = tvm.value; + info->tos_mask = tvm.mask; + if (invert) + info->invert = true; + *flags |= FLAG_TOS; + return true; + } + return false; +} + +static void tos_mt_check(unsigned int flags) +{ + if (flags == 0) + xtables_error(PARAMETER_PROBLEM, + "tos: --tos parameter required"); +} + +static void tos_mt_print_v0(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct ipt_tos_info *info = (const void *)match->data; + + printf("tos match "); + if (info->invert) + printf("!"); + if (numeric || !tos_try_print_symbolic("", info->tos, 0x3F)) + printf("0x%02x ", info->tos); +} + +static void tos_mt_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_tos_match_info *info = (const void *)match->data; + + printf("tos match "); + if (info->invert) + printf("!"); + if (numeric || + !tos_try_print_symbolic("", info->tos_value, info->tos_mask)) + printf("0x%02x/0x%02x ", info->tos_value, info->tos_mask); +} + +static void tos_mt_save_v0(const void *ip, const struct xt_entry_match *match) +{ + const struct ipt_tos_info *info = (const void *)match->data; + + if (info->invert) + printf("! "); + printf("--tos 0x%02x ", info->tos); +} + +static void tos_mt_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_tos_match_info *info = (const void *)match->data; + + if (info->invert) + printf("! "); + printf("--tos 0x%02x/0x%02x ", info->tos_value, info->tos_mask); +} + +static struct xtables_match tos_mt_reg[] = { + { + .version = XTABLES_VERSION, + .name = "tos", + .family = NFPROTO_IPV4, + .revision = 0, + .size = XT_ALIGN(sizeof(struct ipt_tos_info)), + .userspacesize = XT_ALIGN(sizeof(struct ipt_tos_info)), + .help = tos_mt_help, + .parse = tos_mt_parse_v0, + .final_check = tos_mt_check, + .print = tos_mt_print_v0, + .save = tos_mt_save_v0, + .extra_opts = tos_mt_opts, + }, + { + .version = XTABLES_VERSION, + .name = "tos", + .family = NFPROTO_UNSPEC, + .revision = 1, + .size = XT_ALIGN(sizeof(struct xt_tos_match_info)), + .userspacesize = XT_ALIGN(sizeof(struct xt_tos_match_info)), + .help = tos_mt_help, + .parse = tos_mt_parse, + .final_check = tos_mt_check, + .print = tos_mt_print, + .save = tos_mt_save, + .extra_opts = tos_mt_opts, + }, +}; + +void _init(void) +{ + xtables_register_matches(tos_mt_reg, ARRAY_SIZE(tos_mt_reg)); +} diff --git a/extensions/libxt_tos.man b/extensions/libxt_tos.man new file mode 100644 index 0000000..ae73e63 --- /dev/null +++ b/extensions/libxt_tos.man @@ -0,0 +1,12 @@ +This module matches the 8-bit Type of Service field in the IPv4 header (i.e. +including the "Precedence" bits) or the (also 8-bit) Priority field in the IPv6 +header. +.TP +[\fB!\fP] \fB\-\-tos\fP \fIvalue\fP[\fB/\fP\fImask\fP] +Matches packets with the given TOS mark value. If a mask is specified, it is +logically ANDed with the TOS mark before the comparison. +.TP +[\fB!\fP] \fB\-\-tos\fP \fIsymbol\fP +You can specify a symbolic name when using the tos match for IPv4. The list of +recognized TOS names can be obtained by calling iptables with \fB\-m tos \-h\fP. +Note that this implies a mask of 0x3F, i.e. all but the ECN bits. diff --git a/extensions/libxt_u32.c b/extensions/libxt_u32.c new file mode 100644 index 0000000..9a61c8a --- /dev/null +++ b/extensions/libxt_u32.c @@ -0,0 +1,284 @@ +/* Shared library add-on to iptables to add u32 matching, + * generalized matching on values found at packet offsets + * + * Detailed doc is in the kernel module source + * net/netfilter/xt_u32.c + * + * (C) 2002 by Don Cohen <don-netf@isis.cs3-inc.com> + * Released under the terms of GNU GPL v2 + * + * Copyright © CC Computer Consultants GmbH, 2007 + * Contact: <jengelh@computergmbh.de> + */ +#include <sys/types.h> +#include <ctype.h> +#include <errno.h> +#include <getopt.h> +#include <netdb.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <xtables.h> +#include <linux/netfilter/xt_u32.h> + +static const struct option u32_opts[] = { + {"u32", 1, NULL, 'u'}, + { .name = NULL } +}; + +static void u32_help(void) +{ + printf( + "u32 match options:\n" + "[!] --u32 tests\n" + "\t\t""tests := location \"=\" value | tests \"&&\" location \"=\" value\n" + "\t\t""value := range | value \",\" range\n" + "\t\t""range := number | number \":\" number\n" + "\t\t""location := number | location operator number\n" + "\t\t""operator := \"&\" | \"<<\" | \">>\" | \"@\"\n"); +} + +static void u32_dump(const struct xt_u32 *data) +{ + const struct xt_u32_test *ct; + unsigned int testind, i; + + for (testind = 0; testind < data->ntests; ++testind) { + ct = &data->tests[testind]; + + if (testind > 0) + printf("&&"); + + printf("0x%x", ct->location[0].number); + for (i = 1; i < ct->nnums; ++i) { + switch (ct->location[i].nextop) { + case XT_U32_AND: + printf("&"); + break; + case XT_U32_LEFTSH: + printf("<<"); + break; + case XT_U32_RIGHTSH: + printf(">>"); + break; + case XT_U32_AT: + printf("@"); + break; + } + printf("0x%x", ct->location[i].number); + } + + printf("="); + for (i = 0; i < ct->nvalues; ++i) { + if (i > 0) + printf(","); + if (ct->value[i].min == ct->value[i].max) + printf("0x%x", ct->value[i].min); + else + printf("0x%x:0x%x", ct->value[i].min, + ct->value[i].max); + } + } + printf(" "); +} + +/* string_to_number() is not quite what we need here ... */ +static u_int32_t parse_number(char **s, int pos) +{ + u_int32_t number; + char *end; + + errno = 0; + number = strtoul(*s, &end, 0); + if (end == *s) + xtables_error(PARAMETER_PROBLEM, + "u32: at char %d: expected number", pos); + if (errno != 0) + xtables_error(PARAMETER_PROBLEM, + "u32: at char %d: error reading number", pos); + *s = end; + return number; +} + +static int u32_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_u32 *data = (void *)(*match)->data; + unsigned int testind = 0, locind = 0, valind = 0; + struct xt_u32_test *ct = &data->tests[testind]; /* current test */ + char *arg = optarg; /* the argument string */ + char *start = arg; + int state = 0; + + if (c != 'u') + return 0; + + data->invert = invert; + + /* + * states: + * 0 = looking for numbers and operations, + * 1 = looking for ranges + */ + while (1) { + /* read next operand/number or range */ + while (isspace(*arg)) + ++arg; + + if (*arg == '\0') { + /* end of argument found */ + if (state == 0) + xtables_error(PARAMETER_PROBLEM, + "u32: abrupt end of input after location specifier"); + if (valind == 0) + xtables_error(PARAMETER_PROBLEM, + "u32: test ended with no value specified"); + + ct->nnums = locind; + ct->nvalues = valind; + data->ntests = ++testind; + + if (testind > XT_U32_MAXSIZE) + xtables_error(PARAMETER_PROBLEM, + "u32: at char %u: too many \"&&\"s", + (unsigned int)(arg - start)); + return 1; + } + + if (state == 0) { + /* + * reading location: read a number if nothing read yet, + * otherwise either op number or = to end location spec + */ + if (*arg == '=') { + if (locind == 0) { + xtables_error(PARAMETER_PROBLEM, + "u32: at char %u: " + "location spec missing", + (unsigned int)(arg - start)); + } else { + ++arg; + state = 1; + } + } else { + if (locind != 0) { + /* need op before number */ + if (*arg == '&') { + ct->location[locind].nextop = XT_U32_AND; + } else if (*arg == '<') { + if (*++arg != '<') + xtables_error(PARAMETER_PROBLEM, + "u32: at char %u: a second '<' was expected", (unsigned int)(arg - start)); + ct->location[locind].nextop = XT_U32_LEFTSH; + } else if (*arg == '>') { + if (*++arg != '>') + xtables_error(PARAMETER_PROBLEM, + "u32: at char %u: a second '>' was expected", (unsigned int)(arg - start)); + ct->location[locind].nextop = XT_U32_RIGHTSH; + } else if (*arg == '@') { + ct->location[locind].nextop = XT_U32_AT; + } else { + xtables_error(PARAMETER_PROBLEM, + "u32: at char %u: operator expected", (unsigned int)(arg - start)); + } + ++arg; + } + /* now a number; string_to_number skips white space? */ + ct->location[locind].number = + parse_number(&arg, arg - start); + if (++locind > XT_U32_MAXSIZE) + xtables_error(PARAMETER_PROBLEM, + "u32: at char %u: too many operators", (unsigned int)(arg - start)); + } + } else { + /* + * state 1 - reading values: read a range if nothing + * read yet, otherwise either ,range or && to end + * test spec + */ + if (*arg == '&') { + if (*++arg != '&') + xtables_error(PARAMETER_PROBLEM, + "u32: at char %u: a second '&' was expected", (unsigned int)(arg - start)); + if (valind == 0) { + xtables_error(PARAMETER_PROBLEM, + "u32: at char %u: value spec missing", (unsigned int)(arg - start)); + } else { + ct->nnums = locind; + ct->nvalues = valind; + ct = &data->tests[++testind]; + if (testind > XT_U32_MAXSIZE) + xtables_error(PARAMETER_PROBLEM, + "u32: at char %u: too many \"&&\"s", (unsigned int)(arg - start)); + ++arg; + state = 0; + locind = 0; + valind = 0; + } + } else { /* read value range */ + if (valind > 0) { /* need , before number */ + if (*arg != ',') + xtables_error(PARAMETER_PROBLEM, + "u32: at char %u: expected \",\" or \"&&\"", (unsigned int)(arg - start)); + ++arg; + } + ct->value[valind].min = + parse_number(&arg, arg - start); + + while (isspace(*arg)) + ++arg; + + if (*arg == ':') { + ++arg; + ct->value[valind].max = + parse_number(&arg, arg-start); + } else { + ct->value[valind].max = + ct->value[valind].min; + } + + if (++valind > XT_U32_MAXSIZE) + xtables_error(PARAMETER_PROBLEM, + "u32: at char %u: too many \",\"s", (unsigned int)(arg - start)); + } + } + } +} + +static void u32_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_u32 *data = (const void *)match->data; + printf("u32 "); + if (data->invert) + printf("! "); + u32_dump(data); +} + +static void u32_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_u32 *data = (const void *)match->data; + if (data->invert) + printf("! "); + printf("--u32 "); + u32_dump(data); +} + +static struct xtables_match u32_match = { + .name = "u32", + .family = NFPROTO_UNSPEC, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_u32)), + .userspacesize = XT_ALIGN(sizeof(struct xt_u32)), + .help = u32_help, + .parse = u32_parse, + .print = u32_print, + .save = u32_save, + .extra_opts = u32_opts, +}; + +void _init(void) +{ + xtables_register_match(&u32_match); +} diff --git a/extensions/libxt_u32.man b/extensions/libxt_u32.man new file mode 100644 index 0000000..2ffab30 --- /dev/null +++ b/extensions/libxt_u32.man @@ -0,0 +1,129 @@ +U32 tests whether quantities of up to 4 bytes extracted from a packet have +specified values. The specification of what to extract is general enough to +find data at given offsets from tcp headers or payloads. +.TP +[\fB!\fP] \fB\-\-u32\fP \fItests\fP +The argument amounts to a program in a small language described below. +.IP +tests := location "=" value | tests "&&" location "=" value +.IP +value := range | value "," range +.IP +range := number | number ":" number +.PP +a single number, \fIn\fR, is interpreted the same as \fIn:n\fR. \fIn:m\fR is +interpreted as the range of numbers \fB>=n\fR and \fB<=m\fR. +.IP "" 4 +location := number | location operator number +.IP "" 4 +operator := "&" | "<<" | ">>" | "@" +.PP +The operators \fB&\fR, \fB<<\fR, \fB>>\fR and \fB&&\fR mean the same as in C. +The \fB=\fR is really a set membership operator and the value syntax describes +a set. The \fB@\fR operator is what allows moving to the next header and is +described further below. +.PP +There are currently some artificial implementation limits on the size of the +tests: +.IP " *" +no more than 10 of "\fB=\fR" (and 9 "\fB&&\fR"s) in the u32 argument +.IP " *" +no more than 10 ranges (and 9 commas) per value +.IP " *" +no more than 10 numbers (and 9 operators) per location +.PP +To describe the meaning of location, imagine the following machine that +interprets it. There are three registers: +.IP +A is of type \fBchar *\fR, initially the address of the IP header +.IP +B and C are unsigned 32 bit integers, initially zero +.PP +The instructions are: +.IP +number B = number; +.IP +C = (*(A+B)<<24) + (*(A+B+1)<<16) + (*(A+B+2)<<8) + *(A+B+3) +.IP +&number C = C & number +.IP +<< number C = C << number +.IP +>> number C = C >> number +.IP +@number A = A + C; then do the instruction number +.PP +Any access of memory outside [skb\->data,skb\->end] causes the match to fail. +Otherwise the result of the computation is the final value of C. +.PP +Whitespace is allowed but not required in the tests. However, the characters +that do occur there are likely to require shell quoting, so it is a good idea +to enclose the arguments in quotes. +.PP +Example: +.IP +match IP packets with total length >= 256 +.IP +The IP header contains a total length field in bytes 2-3. +.IP +\-\-u32 "\fB0 & 0xFFFF = 0x100:0xFFFF\fP" +.IP +read bytes 0-3 +.IP +AND that with 0xFFFF (giving bytes 2-3), and test whether that is in the range +[0x100:0xFFFF] +.PP +Example: (more realistic, hence more complicated) +.IP +match ICMP packets with icmp type 0 +.IP +First test that it is an ICMP packet, true iff byte 9 (protocol) = 1 +.IP +\-\-u32 "\fB6 & 0xFF = 1 &&\fP ... +.IP +read bytes 6-9, use \fB&\fR to throw away bytes 6-8 and compare the result to +1. Next test that it is not a fragment. (If so, it might be part of such a +packet but we cannot always tell.) N.B.: This test is generally needed if you +want to match anything beyond the IP header. The last 6 bits of byte 6 and all +of byte 7 are 0 iff this is a complete packet (not a fragment). Alternatively, +you can allow first fragments by only testing the last 5 bits of byte 6. +.IP + ... \fB4 & 0x3FFF = 0 &&\fR ... +.IP +Last test: the first byte past the IP header (the type) is 0. This is where we +have to use the @syntax. The length of the IP header (IHL) in 32 bit words is +stored in the right half of byte 0 of the IP header itself. +.IP + ... \fB0 >> 22 & 0x3C @ 0 >> 24 = 0\fR" +.IP +The first 0 means read bytes 0-3, \fB>>22\fR means shift that 22 bits to the +right. Shifting 24 bits would give the first byte, so only 22 bits is four +times that plus a few more bits. \fB&3C\fR then eliminates the two extra bits +on the right and the first four bits of the first byte. For instance, if IHL=5, +then the IP header is 20 (4 x 5) bytes long. In this case, bytes 0-1 are (in +binary) xxxx0101 yyzzzzzz, \fB>>22\fR gives the 10 bit value xxxx0101yy and +\fB&3C\fR gives 010100. \fB@\fR means to use this number as a new offset into +the packet, and read four bytes starting from there. This is the first 4 bytes +of the ICMP payload, of which byte 0 is the ICMP type. Therefore, we simply +shift the value 24 to the right to throw out all but the first byte and compare +the result with 0. +.PP +Example: +.IP +TCP payload bytes 8-12 is any of 1, 2, 5 or 8 +.IP +First we test that the packet is a tcp packet (similar to ICMP). +.IP +\-\-u32 "\fB6 & 0xFF = 6 &&\fP ... +.IP +Next, test that it is not a fragment (same as above). +.IP + ... \fB0 >> 22 & 0x3C @ 12 >> 26 & 0x3C @ 8 = 1,2,5,8\fR" +.IP +\fB0>>22&3C\fR as above computes the number of bytes in the IP header. \fB@\fR +makes this the new offset into the packet, which is the start of the TCP +header. The length of the TCP header (again in 32 bit words) is the left half +of byte 12 of the TCP header. The \fB12>>26&3C\fR computes this length in bytes +(similar to the IP header before). "@" makes this the new offset, which is the +start of the TCP payload. Finally, 8 reads bytes 8-12 of the payload and +\fB=\fR checks whether the result is any of 1, 2, 5 or 8. diff --git a/extensions/libxt_udp.c b/extensions/libxt_udp.c new file mode 100644 index 0000000..135e7af --- /dev/null +++ b/extensions/libxt_udp.c @@ -0,0 +1,211 @@ +/* Shared library add-on to iptables to add UDP support. */ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <netinet/in.h> +#include <xtables.h> +#include <linux/netfilter/xt_tcpudp.h> + +static void udp_help(void) +{ + printf( +"udp match options:\n" +"[!] --source-port port[:port]\n" +" --sport ...\n" +" match source port(s)\n" +"[!] --destination-port port[:port]\n" +" --dport ...\n" +" match destination port(s)\n"); +} + +static const struct option udp_opts[] = { + { "source-port", 1, NULL, '1' }, + { "sport", 1, NULL, '1' }, /* synonym */ + { "destination-port", 1, NULL, '2' }, + { "dport", 1, NULL, '2' }, /* synonym */ + { .name = NULL } +}; + +static void +parse_udp_ports(const char *portstring, u_int16_t *ports) +{ + char *buffer; + char *cp; + + buffer = strdup(portstring); + if ((cp = strchr(buffer, ':')) == NULL) + ports[0] = ports[1] = xtables_parse_port(buffer, "udp"); + else { + *cp = '\0'; + cp++; + + ports[0] = buffer[0] ? xtables_parse_port(buffer, "udp") : 0; + ports[1] = cp[0] ? xtables_parse_port(cp, "udp") : 0xFFFF; + + if (ports[0] > ports[1]) + xtables_error(PARAMETER_PROBLEM, + "invalid portrange (min > max)"); + } + free(buffer); +} + +static void udp_init(struct xt_entry_match *m) +{ + struct xt_udp *udpinfo = (struct xt_udp *)m->data; + + udpinfo->spts[1] = udpinfo->dpts[1] = 0xFFFF; +} + +#define UDP_SRC_PORTS 0x01 +#define UDP_DST_PORTS 0x02 + +static int +udp_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_udp *udpinfo = (struct xt_udp *)(*match)->data; + + switch (c) { + case '1': + if (*flags & UDP_SRC_PORTS) + xtables_error(PARAMETER_PROBLEM, + "Only one `--source-port' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_udp_ports(optarg, udpinfo->spts); + if (invert) + udpinfo->invflags |= XT_UDP_INV_SRCPT; + *flags |= UDP_SRC_PORTS; + break; + + case '2': + if (*flags & UDP_DST_PORTS) + xtables_error(PARAMETER_PROBLEM, + "Only one `--destination-port' allowed"); + xtables_check_inverse(optarg, &invert, &optind, 0, argv); + parse_udp_ports(optarg, udpinfo->dpts); + if (invert) + udpinfo->invflags |= XT_UDP_INV_DSTPT; + *flags |= UDP_DST_PORTS; + break; + + default: + return 0; + } + + return 1; +} + +static char * +port_to_service(int port) +{ + struct servent *service; + + if ((service = getservbyport(htons(port), "udp"))) + return service->s_name; + + return NULL; +} + +static void +print_port(u_int16_t port, int numeric) +{ + char *service; + + if (numeric || (service = port_to_service(port)) == NULL) + printf("%u", port); + else + printf("%s", service); +} + +static void +print_ports(const char *name, u_int16_t min, u_int16_t max, + int invert, int numeric) +{ + const char *inv = invert ? "!" : ""; + + if (min != 0 || max != 0xFFFF || invert) { + printf("%s", name); + if (min == max) { + printf(":%s", inv); + print_port(min, numeric); + } else { + printf("s:%s", inv); + print_port(min, numeric); + printf(":"); + print_port(max, numeric); + } + printf(" "); + } +} + +static void +udp_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_udp *udp = (struct xt_udp *)match->data; + + printf("udp "); + print_ports("spt", udp->spts[0], udp->spts[1], + udp->invflags & XT_UDP_INV_SRCPT, + numeric); + print_ports("dpt", udp->dpts[0], udp->dpts[1], + udp->invflags & XT_UDP_INV_DSTPT, + numeric); + if (udp->invflags & ~XT_UDP_INV_MASK) + printf("Unknown invflags: 0x%X ", + udp->invflags & ~XT_UDP_INV_MASK); +} + +static void udp_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_udp *udpinfo = (struct xt_udp *)match->data; + + if (udpinfo->spts[0] != 0 + || udpinfo->spts[1] != 0xFFFF) { + if (udpinfo->invflags & XT_UDP_INV_SRCPT) + printf("! "); + if (udpinfo->spts[0] + != udpinfo->spts[1]) + printf("--sport %u:%u ", + udpinfo->spts[0], + udpinfo->spts[1]); + else + printf("--sport %u ", + udpinfo->spts[0]); + } + + if (udpinfo->dpts[0] != 0 + || udpinfo->dpts[1] != 0xFFFF) { + if (udpinfo->invflags & XT_UDP_INV_DSTPT) + printf("! "); + if (udpinfo->dpts[0] + != udpinfo->dpts[1]) + printf("--dport %u:%u ", + udpinfo->dpts[0], + udpinfo->dpts[1]); + else + printf("--dport %u ", + udpinfo->dpts[0]); + } +} + +static struct xtables_match udp_match = { + .family = NFPROTO_UNSPEC, + .name = "udp", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_udp)), + .userspacesize = XT_ALIGN(sizeof(struct xt_udp)), + .help = udp_help, + .init = udp_init, + .parse = udp_parse, + .print = udp_print, + .save = udp_save, + .extra_opts = udp_opts, +}; + +void +_init(void) +{ + xtables_register_match(&udp_match); +} diff --git a/extensions/libxt_udp.man b/extensions/libxt_udp.man new file mode 100644 index 0000000..5339c8e --- /dev/null +++ b/extensions/libxt_udp.man @@ -0,0 +1,14 @@ +These extensions can be used if `\-\-protocol udp' is specified. It +provides the following options: +.TP +[\fB!\fP] \fB\-\-source\-port\fP,\fB\-\-sport\fP \fIport\fP[\fB:\fP\fIport\fP] +Source port or port range specification. +See the description of the +\fB\-\-source\-port\fP +option of the TCP extension for details. +.TP +[\fB!\fP] \fB\-\-destination\-port\fP,\fB\-\-dport\fP \fIport\fP[\fB:\fP\fIport\fP] +Destination port or port range specification. +See the description of the +\fB\-\-destination\-port\fP +option of the TCP extension for details. diff --git a/extensions/tos_values.c b/extensions/tos_values.c new file mode 100644 index 0000000..e8f1563 --- /dev/null +++ b/extensions/tos_values.c @@ -0,0 +1,96 @@ +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <linux/ip.h> + +#ifndef IPTOS_NORMALSVC +# define IPTOS_NORMALSVC 0 +#endif + +struct tos_value_mask { + uint8_t value, mask; +}; + +static const struct tos_symbol_info { + unsigned char value; + const char *name; +} tos_symbol_names[] = { + {IPTOS_LOWDELAY, "Minimize-Delay"}, + {IPTOS_THROUGHPUT, "Maximize-Throughput"}, + {IPTOS_RELIABILITY, "Maximize-Reliability"}, + {IPTOS_MINCOST, "Minimize-Cost"}, + {IPTOS_NORMALSVC, "Normal-Service"}, + { .name = NULL } +}; + +/* + * tos_parse_numeric - parse sth. like "15/255" + * + * @s: input string + * @info: accompanying structure + * @bits: number of bits that are allowed + * (8 for IPv4 TOS field, 4 for IPv6 Priority Field) + */ +static bool tos_parse_numeric(const char *str, struct tos_value_mask *tvm, + unsigned int bits) +{ + const unsigned int max = (1 << bits) - 1; + unsigned int value; + char *end; + + xtables_strtoui(str, &end, &value, 0, max); + tvm->value = value; + tvm->mask = max; + + if (*end == '/') { + const char *p = end + 1; + + if (!xtables_strtoui(p, &end, &value, 0, max)) + xtables_error(PARAMETER_PROBLEM, "Illegal value: \"%s\"", + str); + tvm->mask = value; + } + + if (*end != '\0') + xtables_error(PARAMETER_PROBLEM, "Illegal value: \"%s\"", str); + return true; +} + +static bool tos_parse_symbolic(const char *str, struct tos_value_mask *tvm, + unsigned int def_mask) +{ + const unsigned int max = UINT8_MAX; + const struct tos_symbol_info *symbol; + char *tmp; + + if (xtables_strtoui(str, &tmp, NULL, 0, max)) + return tos_parse_numeric(str, tvm, max); + + /* Do not consider ECN bits */ + tvm->mask = def_mask; + for (symbol = tos_symbol_names; symbol->name != NULL; ++symbol) + if (strcasecmp(str, symbol->name) == 0) { + tvm->value = symbol->value; + return true; + } + + xtables_error(PARAMETER_PROBLEM, "Symbolic name \"%s\" is unknown", str); + return false; +} + +static bool tos_try_print_symbolic(const char *prefix, + u_int8_t value, u_int8_t mask) +{ + const struct tos_symbol_info *symbol; + + if (mask != 0x3F) + return false; + + for (symbol = tos_symbol_names; symbol->name != NULL; ++symbol) + if (value == symbol->value) { + printf("%s%s ", prefix, symbol->name); + return true; + } + + return false; +} |