diff options
Diffstat (limited to 'net/netfilter')
-rw-r--r-- | net/netfilter/xt_iprange.c | 122 |
1 files changed, 113 insertions, 9 deletions
diff --git a/net/netfilter/xt_iprange.c b/net/netfilter/xt_iprange.c index c57a6cf8a08..dbea0e0893f 100644 --- a/net/netfilter/xt_iprange.c +++ b/net/netfilter/xt_iprange.c @@ -2,6 +2,7 @@ * xt_iprange - Netfilter module to match IP address ranges * * (C) 2003 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> + * (C) CC Computer Consultants GmbH, 2008 * * 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 @@ -10,6 +11,7 @@ #include <linux/module.h> #include <linux/skbuff.h> #include <linux/ip.h> +#include <linux/ipv6.h> #include <linux/netfilter/x_tables.h> #include <linux/netfilter_ipv4/ipt_iprange.h> @@ -51,26 +53,128 @@ iprange_mt_v0(const struct sk_buff *skb, const struct net_device *in, return true; } -static struct xt_match iprange_mt_reg __read_mostly = { - .name = "iprange", - .family = AF_INET, - .match = iprange_mt_v0, - .matchsize = sizeof(struct ipt_iprange_info), - .me = THIS_MODULE +static bool +iprange_mt4(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const struct xt_match *match, + const void *matchinfo, int offset, unsigned int protoff, + bool *hotdrop) +{ + const struct xt_iprange_mtinfo *info = matchinfo; + const struct iphdr *iph = ip_hdr(skb); + bool m; + + if (info->flags & IPRANGE_SRC) { + m = ntohl(iph->saddr) < ntohl(info->src_min.ip); + m |= ntohl(iph->saddr) > ntohl(info->src_max.ip); + m ^= info->flags & IPRANGE_SRC_INV; + if (m) { + pr_debug("src IP " NIPQUAD_FMT " NOT in range %s" + NIPQUAD_FMT "-" NIPQUAD_FMT "\n", + NIPQUAD(iph->saddr), + (info->flags & IPRANGE_SRC_INV) ? "(INV) " : "", + NIPQUAD(info->src_max.ip), + NIPQUAD(info->src_max.ip)); + return false; + } + } + if (info->flags & IPRANGE_DST) { + m = ntohl(iph->daddr) < ntohl(info->dst_min.ip); + m |= ntohl(iph->daddr) > ntohl(info->dst_max.ip); + m ^= info->flags & IPRANGE_DST_INV; + if (m) { + pr_debug("dst IP " NIPQUAD_FMT " NOT in range %s" + NIPQUAD_FMT "-" NIPQUAD_FMT "\n", + NIPQUAD(iph->daddr), + (info->flags & IPRANGE_DST_INV) ? "(INV) " : "", + NIPQUAD(info->dst_min.ip), + NIPQUAD(info->dst_max.ip)); + return false; + } + } + return true; +} + +static inline int +iprange_ipv6_sub(const struct in6_addr *a, const struct in6_addr *b) +{ + unsigned int i; + int r; + + for (i = 0; i < 4; ++i) { + r = a->s6_addr32[i] - b->s6_addr32[i]; + if (r != 0) + return r; + } + + return 0; +} + +static bool +iprange_mt6(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const struct xt_match *match, + const void *matchinfo, int offset, unsigned int protoff, + bool *hotdrop) +{ + const struct xt_iprange_mtinfo *info = matchinfo; + const struct ipv6hdr *iph = ipv6_hdr(skb); + bool m; + + if (info->flags & IPRANGE_SRC) { + m = iprange_ipv6_sub(&iph->saddr, &info->src_min.in6) < 0; + m |= iprange_ipv6_sub(&iph->saddr, &info->src_max.in6) > 0; + m ^= info->flags & IPRANGE_SRC_INV; + if (m) + return false; + } + if (info->flags & IPRANGE_DST) { + m = iprange_ipv6_sub(&iph->daddr, &info->dst_min.in6) < 0; + m |= iprange_ipv6_sub(&iph->daddr, &info->dst_max.in6) > 0; + m ^= info->flags & IPRANGE_DST_INV; + if (m) + return false; + } + return true; +} + +static struct xt_match iprange_mt_reg[] __read_mostly = { + { + .name = "iprange", + .revision = 0, + .family = AF_INET, + .match = iprange_mt_v0, + .matchsize = sizeof(struct ipt_iprange_info), + .me = THIS_MODULE, + }, + { + .name = "iprange", + .revision = 1, + .family = AF_INET6, + .match = iprange_mt4, + .matchsize = sizeof(struct xt_iprange_mtinfo), + .me = THIS_MODULE, + }, + { + .name = "iprange", + .revision = 1, + .family = AF_INET6, + .match = iprange_mt6, + .matchsize = sizeof(struct xt_iprange_mtinfo), + .me = THIS_MODULE, + }, }; static int __init iprange_mt_init(void) { - return xt_register_match(&iprange_mt_reg); + return xt_register_matches(iprange_mt_reg, ARRAY_SIZE(iprange_mt_reg)); } static void __exit iprange_mt_exit(void) { - xt_unregister_match(&iprange_mt_reg); + xt_unregister_matches(iprange_mt_reg, ARRAY_SIZE(iprange_mt_reg)); } module_init(iprange_mt_init); module_exit(iprange_mt_exit); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); +MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>, Jan Engelhardt <jengelh@computergmbh.de>"); MODULE_DESCRIPTION("Xtables: arbitrary IPv4 range matching"); |