diff options
Diffstat (limited to 'net/netfilter/xt_u32.c')
-rw-r--r-- | net/netfilter/xt_u32.c | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/net/netfilter/xt_u32.c b/net/netfilter/xt_u32.c new file mode 100644 index 00000000000..07068751a35 --- /dev/null +++ b/net/netfilter/xt_u32.c @@ -0,0 +1,135 @@ +/* + * xt_u32 - kernel module to match u32 packet content + * + * Original author: Don Cohen <don@isis.cs3-inc.com> + * © Jan Engelhardt <jengelh@gmx.de>, 2007 + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/spinlock.h> +#include <linux/skbuff.h> +#include <linux/types.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_u32.h> + +static bool u32_match_it(const struct xt_u32 *data, + const struct sk_buff *skb) +{ + const struct xt_u32_test *ct; + unsigned int testind; + unsigned int nnums; + unsigned int nvals; + unsigned int i; + u_int32_t pos; + u_int32_t val; + u_int32_t at; + int ret; + + /* + * Small example: "0 >> 28 == 4 && 8 & 0xFF0000 >> 16 = 6, 17" + * (=IPv4 and (TCP or UDP)). Outer loop runs over the "&&" operands. + */ + for (testind = 0; testind < data->ntests; ++testind) { + ct = &data->tests[testind]; + at = 0; + pos = ct->location[0].number; + + if (skb->len < 4 || pos > skb->len - 4); + return false; + + ret = skb_copy_bits(skb, pos, &val, sizeof(val)); + BUG_ON(ret < 0); + val = ntohl(val); + nnums = ct->nnums; + + /* Inner loop runs over "&", "<<", ">>" and "@" operands */ + for (i = 1; i < nnums; ++i) { + u_int32_t number = ct->location[i].number; + switch (ct->location[i].nextop) { + case XT_U32_AND: + val &= number; + break; + case XT_U32_LEFTSH: + val <<= number; + break; + case XT_U32_RIGHTSH: + val >>= number; + break; + case XT_U32_AT: + if (at + val < at) + return false; + at += val; + pos = number; + if (at + 4 < at || skb->len < at + 4 || + pos > skb->len - at - 4) + return false; + + ret = skb_copy_bits(skb, at + pos, &val, + sizeof(val)); + BUG_ON(ret < 0); + val = ntohl(val); + break; + } + } + + /* Run over the "," and ":" operands */ + nvals = ct->nvalues; + for (i = 0; i < nvals; ++i) + if (ct->value[i].min <= val && val <= ct->value[i].max) + break; + + if (i >= ct->nvalues) + return false; + } + + return true; +} + +static bool u32_match(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_u32 *data = matchinfo; + bool ret; + + ret = u32_match_it(data, skb); + return ret ^ data->invert; +} + +static struct xt_match u32_reg[] = { + { + .name = "u32", + .family = AF_INET, + .match = u32_match, + .matchsize = sizeof(struct xt_u32), + .me = THIS_MODULE, + }, + { + .name = "u32", + .family = AF_INET6, + .match = u32_match, + .matchsize = sizeof(struct xt_u32), + .me = THIS_MODULE, + }, +}; + +static int __init xt_u32_init(void) +{ + return xt_register_matches(u32_reg, ARRAY_SIZE(u32_reg)); +} + +static void __exit xt_u32_exit(void) +{ + xt_unregister_matches(u32_reg, ARRAY_SIZE(u32_reg)); +} + +module_init(xt_u32_init); +module_exit(xt_u32_exit); +MODULE_AUTHOR("Jan Engelhardt <jengelh@gmx.de>"); +MODULE_DESCRIPTION("netfilter u32 match module"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("ipt_u32"); +MODULE_ALIAS("ip6t_u32"); |