diff --git a/doc/changelog.txt b/doc/changelog.txt index 7e833fa..e77d309 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -6,6 +6,7 @@ - fuzzy: fix bogus comparison logic leftover from move to new 1.4.3 API - ipp2p: fix bogus varargs call - ipp2p: fix typo in error message +- add "iface" match - added rawpost table (for use with RAWNAT) - added RAWSNAT/RAWDNAT targets diff --git a/extensions/Kbuild b/extensions/Kbuild index 019a28a..64c7d35 100644 --- a/extensions/Kbuild +++ b/extensions/Kbuild @@ -19,6 +19,7 @@ obj-${build_TEE} += xt_TEE.o obj-${build_condition} += xt_condition.o obj-${build_fuzzy} += xt_fuzzy.o obj-${build_geoip} += xt_geoip.o +obj-${build_iface} += xt_iface.o obj-${build_ipp2p} += xt_ipp2p.o obj-${build_ipset} += ipset/ obj-${build_ipv4options} += xt_ipv4options.o diff --git a/extensions/Mbuild b/extensions/Mbuild index 294844b..0a24854 100644 --- a/extensions/Mbuild +++ b/extensions/Mbuild @@ -12,6 +12,7 @@ obj-${build_TEE} += libxt_TEE.so obj-${build_condition} += libxt_condition.so obj-${build_fuzzy} += libxt_fuzzy.so obj-${build_geoip} += libxt_geoip.so +obj-${build_iface} += libxt_iface.so obj-${build_ipp2p} += libxt_ipp2p.so obj-${build_ipset} += ipset/ obj-${build_ipv4options} += libxt_ipv4options.so diff --git a/extensions/libxt_iface.c b/extensions/libxt_iface.c new file mode 100644 index 0000000..091951e --- /dev/null +++ b/extensions/libxt_iface.c @@ -0,0 +1,233 @@ +/* + * Shared library add-on to iptables to add interface state matching + * support. + * + * (C) 2008 Gáspár Lajos + * + * This program is released under the terms of GNU GPL version 2. + */ + +#include +#include +#include +#include +#include + +#include +#include "xt_iface.h" + +static const struct option iface_mt_opts[] = { + {.name = "iface", .has_arg = true, .val = 'i'}, + {.name = "up", .has_arg = false, .val = 'u'}, + {.name = "down", .has_arg = false, .val = 'U'}, /* not up */ + {.name = "broadcast", .has_arg = false, .val = 'b'}, + {.name = "loopback", .has_arg = false, .val = 'l'}, + {.name = "pointopoint", .has_arg = false, .val = 'p'}, + {.name = "pointtopoint", .has_arg = false, .val = 'p'}, /* eq pointopoint */ + {.name = "running", .has_arg = false, .val = 'r'}, + {.name = "noarp", .has_arg = false, .val = 'n'}, + {.name = "arp", .has_arg = false, .val = 'N'}, /* not noarp */ + {.name = "promisc", .has_arg = false, .val = 'o'}, + {.name = "multicast", .has_arg = false, .val = 'm'}, + {.name = "dynamic", .has_arg = false, .val = 'd'}, + {.name = "lower-up", .has_arg = false, .val = 'w'}, + {.name = "dormant", .has_arg = false, .val = 'a'}, + {NULL}, +}; + +static void iface_print_opt(const struct xt_iface_mtinfo *info, + const unsigned int option, const char *command) +{ + if (info->flags & option) + printf(" %s", command); + if (info->invflags & option) + printf(" ! %s", command); +} + +static void iface_setflag(struct xt_iface_mtinfo *info, + unsigned int *flags, int invert, u_int16_t flag, const char *command) +{ + if (*flags & flag) + xtables_error(PARAMETER_PROBLEM, + "iface: \"--%s\" flag already specified", command); + if (invert) + info->invflags |= flag; + else + info->flags |= flag; + *flags |= flag; +} + +static bool iface_valid_name(const char *name) +{ + static const char invalid_chars[] = ".+!*"; + + return strlen(name) < IFNAMSIZ && strpbrk(name, invalid_chars) == NULL; +} + +static void iface_mt_help(void) +{ + printf( + "iface match options:\n" + " --iface interface Name of interface\n" + "[!] --up / --down match if UP flag (not) set\n" + "[!] --broadcast match if BROADCAST flag (not) set\n" + "[!] --loopback match if LOOPBACK flag (not) set\n" + "[!] --pointopoint\n" + "[!] --pointtopoint match if POINTOPOINT flag (not) set\n" + "[!] --running match if RUNNING flag (not) set\n" + "[!] --noarp / --arp match if NOARP flag (not) set\n" + "[!] --promisc match if PROMISC flag (not) set\n" + "[!] --multicast match if MULTICAST flag (not) set\n" + "[!] --dynamic match if DYNAMIC flag (not) set\n" + "[!] --lower-up match if LOWER_UP flag (not) set\n" + "[!] --dormant match if DORMANT flag (not) set\n"); +} + +static int iface_mt_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_iface_mtinfo *info = (void *)(*match)->data; + + switch (c) { + case 'U': + c = 'u'; + invert = !invert; + break; + case 'N': + c = 'n'; + invert = !invert; + break; + } + + switch (c) { + case 'i': /* interface name */ + if (*flags & XT_IFACE_IFACE) + xtables_error(PARAMETER_PROBLEM, + "iface: Interface name already specified"); + if (!iface_valid_name(optarg)) + xtables_error(PARAMETER_PROBLEM, + "iface: Invalid interface name!"); + strcpy(info->ifname, optarg); + *flags |= XT_IFACE_IFACE; + return true; + case 'u': /* UP */ + iface_setflag(info, flags, invert, XT_IFACE_UP, "up"); + return true; + case 'b': /* BROADCAST */ + iface_setflag(info, flags, invert, XT_IFACE_BROADCAST, "broadcast"); + return true; + case 'l': /* LOOPBACK */ + iface_setflag(info, flags, invert, XT_IFACE_LOOPBACK, "loopback"); + return true; + case 'p': /* POINTOPOINT */ + iface_setflag(info, flags, invert, XT_IFACE_POINTOPOINT, "pointopoint"); + return true; + case 'r': /* RUNNING */ + iface_setflag(info, flags, invert, XT_IFACE_RUNNING, "running"); + return true; + case 'n': /* NOARP */ + iface_setflag(info, flags, invert, XT_IFACE_NOARP, "noarp"); + return true; + case 'o': /* PROMISC */ + iface_setflag(info, flags, invert, XT_IFACE_PROMISC, "promisc"); + return true; + case 'm': /* MULTICAST */ + iface_setflag(info, flags, invert, XT_IFACE_MULTICAST, "multicast"); + return true; + case 'd': /* DYNAMIC */ + iface_setflag(info, flags, invert, XT_IFACE_DYNAMIC, "dynamic"); + return true; + case 'w': /* LOWER_UP */ + iface_setflag(info, flags, invert, XT_IFACE_LOWER_UP, "lower_up"); + return true; + case 'a': /* DORMANT */ + iface_setflag(info, flags, invert, XT_IFACE_DORMANT, "dormant"); + return true; + } + return false; +} + +static void iface_mt_check(unsigned int flags) +{ + if (!(flags & XT_IFACE_IFACE)) + xtables_error(PARAMETER_PROBLEM, + "iface: You must specify an interface"); + if (flags == 0 || flags == XT_IFACE_IFACE) + xtables_error(PARAMETER_PROBLEM, + "iface: You must specify at least one option"); +} + +static void iface_mt_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_iface_mtinfo *info = (const void *)match->data; + + printf("iface: \"%s\" [state:", info->ifname); + iface_print_opt(info, XT_IFACE_UP, "up"); + iface_print_opt(info, XT_IFACE_BROADCAST, "broadcast"); + iface_print_opt(info, XT_IFACE_LOOPBACK, "loopback"); + iface_print_opt(info, XT_IFACE_POINTOPOINT, "pointopoint"); + iface_print_opt(info, XT_IFACE_RUNNING, "running"); + iface_print_opt(info, XT_IFACE_NOARP, "noarp"); + iface_print_opt(info, XT_IFACE_PROMISC, "promisc"); + iface_print_opt(info, XT_IFACE_MULTICAST, "multicast"); + iface_print_opt(info, XT_IFACE_DYNAMIC, "dynamic"); + iface_print_opt(info, XT_IFACE_LOWER_UP, "lower_up"); + iface_print_opt(info, XT_IFACE_DORMANT, "dormant"); + printf("] "); +} + +static void iface_mt_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_iface_mtinfo *info = (const void *)match->data; + + printf(" --iface %s", info->ifname); + iface_print_opt(info, XT_IFACE_UP, "--up"); + iface_print_opt(info, XT_IFACE_BROADCAST, "--broadcast"); + iface_print_opt(info, XT_IFACE_LOOPBACK, "--loopback"); + iface_print_opt(info, XT_IFACE_POINTOPOINT, "--pointopoint"); + iface_print_opt(info, XT_IFACE_RUNNING, "--running"); + iface_print_opt(info, XT_IFACE_NOARP, "--noarp"); + iface_print_opt(info, XT_IFACE_PROMISC, "--promisc"); + iface_print_opt(info, XT_IFACE_MULTICAST, "--multicast"); + iface_print_opt(info, XT_IFACE_DYNAMIC, "--dynamic"); + iface_print_opt(info, XT_IFACE_LOWER_UP, "--lower_up"); + iface_print_opt(info, XT_IFACE_DORMANT, "--dormant"); + printf(" "); +} + +static struct xtables_match iface_mt_reg = { + .version = XTABLES_VERSION, + .name = "iface", + .revision = 0, + .family = AF_INET, + .size = XT_ALIGN(sizeof(struct xt_iface_mtinfo)), + .userspacesize = XT_ALIGN(sizeof(struct xt_iface_mtinfo)), + .help = iface_mt_help, + .parse = iface_mt_parse, + .final_check = iface_mt_check, + .print = iface_mt_print, + .save = iface_mt_save, + .extra_opts = iface_mt_opts, +}; + +static struct xtables_match iface_mt6_reg = { + .version = XTABLES_VERSION, + .name = "iface", + .revision = 0, + .family = AF_INET6, + .size = XT_ALIGN(sizeof(struct xt_iface_mtinfo)), + .userspacesize = XT_ALIGN(sizeof(struct xt_iface_mtinfo)), + .help = iface_mt_help, + .parse = iface_mt_parse, + .final_check = iface_mt_check, + .print = iface_mt_print, + .save = iface_mt_save, + .extra_opts = iface_mt_opts, +}; + +static void _init(void) +{ + xtables_register_match(&iface_mt_reg); + xtables_register_match(&iface_mt6_reg); +} diff --git a/extensions/libxt_iface.man b/extensions/libxt_iface.man new file mode 100644 index 0000000..7dc6820 --- /dev/null +++ b/extensions/libxt_iface.man @@ -0,0 +1,37 @@ +Allows you to check interface states. +.TP +\fB\-\-iface\fP \fIname\fP +Check the states on the given interface. This option is required. +.TP +[\fB!\fP] \fB\-\-up\fP, [\fB!\fP] \fB\-\-down\fP +Check the UP flag. +.TP +[\fB!\fP] \fB\-\-broadcast\fP +Check the BROADCAST flag. +.TP +[\fB!\fP] \fB\-\-loopback\fP +Check the LOOPBACK flag. +.TP +[\fB!\fP] \fB\-\-pointtopoint\fP +Check the POINTTOPOINT flag. +.TP +[\fB!\fP] \fB\-\-running\fP +Check the RUNNING flag. Do NOT rely on it! +.TP +[\fB!\fP] \fB\-\-noarp\fP, [\fB!\fP] \fB\-\-arp\fP +Check the NOARP flag. +.TP +[\fB!\fP] \fB\-\-promisc\fP +Check the PROMISC flag. +.TP +[\fB!\fP] \fB\-\-multicast\fP +Check the MULTICAST flag. +.TP +[\fB!\fP] \fB\-\-dynamic\fP +Check the DYNAMIC flag. +.TP +[\fB!\fP] \fB\-\-lower-up\fP +Check the LOWER_UP flag. +.TP +[\fB!\fP] \fB\-\-dormant\fP +Check the DORMANT flag. diff --git a/extensions/xt_iface.c b/extensions/xt_iface.c new file mode 100644 index 0000000..df3e4a3 --- /dev/null +++ b/extensions/xt_iface.c @@ -0,0 +1,97 @@ +/* + * xt_iface - kernel module to match interface state flags + * + * Original author: Gáspár Lajos + */ + +#include +#include +#include +#include +#include +#include +#include "xt_iface.h" + +struct xt_iface_flag_pairs { + uint16_t iface_flag; + uint32_t iff_flag; +}; + +MODULE_AUTHOR("Gáspár Lajos "); +MODULE_DESCRIPTION("Xtables: iface match module"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_iface"); +MODULE_ALIAS("ip6t_iface"); +//MODULE_ALIAS("arpt_iface"); + +static const struct xt_iface_flag_pairs xt_iface_lookup[] = +{ + {.iface_flag = XT_IFACE_UP, .iff_flag = IFF_UP}, + {.iface_flag = XT_IFACE_BROADCAST, .iff_flag = IFF_BROADCAST}, + {.iface_flag = XT_IFACE_LOOPBACK, .iff_flag = IFF_LOOPBACK}, + {.iface_flag = XT_IFACE_POINTOPOINT, .iff_flag = IFF_POINTOPOINT}, + {.iface_flag = XT_IFACE_RUNNING, .iff_flag = IFF_RUNNING}, + {.iface_flag = XT_IFACE_NOARP, .iff_flag = IFF_NOARP}, + {.iface_flag = XT_IFACE_PROMISC, .iff_flag = IFF_PROMISC}, + {.iface_flag = XT_IFACE_MULTICAST, .iff_flag = IFF_MULTICAST}, + {.iface_flag = XT_IFACE_DYNAMIC, .iff_flag = IFF_DYNAMIC}, + {.iface_flag = XT_IFACE_LOWER_UP, .iff_flag = IFF_LOWER_UP}, + {.iface_flag = XT_IFACE_DORMANT, .iff_flag = IFF_DORMANT}, +}; + +static bool xt_iface_mt(const struct sk_buff *skb, + const struct xt_match_param *par) +{ + const struct xt_iface_mtinfo *info = par->matchinfo; + struct net_device *dev; + bool retval; + int i; + + dev = dev_get_by_name(&init_net, info->ifname); + retval = dev != NULL; + if (retval) { + for (i = 0; i < ARRAY_SIZE(xt_iface_lookup) && retval; ++i) { + if (info->flags & xt_iface_lookup[i].iface_flag) + retval &= dev->flags & xt_iface_lookup[i].iff_flag; + if (info->invflags & xt_iface_lookup[i].iface_flag) + retval &= !(dev->flags & xt_iface_lookup[i].iff_flag); + } + dev_put(dev); + } + return retval; +} + +static struct xt_match xt_iface_mt_reg[] __read_mostly = { + { + .name = "iface", + .revision = 0, + .family = NFPROTO_IPV4, + .matchsize = XT_ALIGN(sizeof(struct xt_iface_mtinfo)), + .match = xt_iface_mt, + .data = 0, + .me = THIS_MODULE, + }, + { + .name = "iface", + .revision = 0, + .family = NFPROTO_IPV6, + .matchsize = XT_ALIGN(sizeof(struct xt_iface_mtinfo)), + .match = xt_iface_mt, + .data = 0, + .me = THIS_MODULE, + }, +}; + +static int __init xt_iface_match_init(void) +{ + return xt_register_matches(xt_iface_mt_reg, + ARRAY_SIZE(xt_iface_mt_reg)); +} + +static void __exit xt_iface_match_exit(void) +{ + xt_unregister_matches(xt_iface_mt_reg, ARRAY_SIZE(xt_iface_mt_reg)); +} + +module_init(xt_iface_match_init); +module_exit(xt_iface_match_exit); diff --git a/extensions/xt_iface.h b/extensions/xt_iface.h new file mode 100644 index 0000000..f1dbba7 --- /dev/null +++ b/extensions/xt_iface.h @@ -0,0 +1,25 @@ +#ifndef _LINUX_NETFILTER_XT_IFACE_H +#define _LINUX_NETFILTER_XT_IFACE_H 1 + +enum { + XT_IFACE_UP = 1 << 0, + XT_IFACE_BROADCAST = 1 << 1, + XT_IFACE_LOOPBACK = 1 << 2, + XT_IFACE_POINTOPOINT = 1 << 3, + XT_IFACE_RUNNING = 1 << 4, + XT_IFACE_NOARP = 1 << 5, + XT_IFACE_PROMISC = 1 << 6, + XT_IFACE_MULTICAST = 1 << 7, + XT_IFACE_DYNAMIC = 1 << 8, + XT_IFACE_LOWER_UP = 1 << 9, + XT_IFACE_DORMANT = 1 << 10, + XT_IFACE_IFACE = 1 << 15, +}; + +struct xt_iface_mtinfo { + char ifname[IFNAMSIZ]; + __u16 flags; + __u16 invflags; +}; + +#endif diff --git a/mconfig b/mconfig index dfa46c4..47761a8 100644 --- a/mconfig +++ b/mconfig @@ -14,6 +14,7 @@ build_TEE=m build_condition=m build_fuzzy=m build_geoip=m +build_iface=m build_ipp2p=m build_ipset=m build_ipv4options=m