commit ad446d5b2c0b32ead9dd86b9c10356c4617eeaf5 Author: Jan Engelhardt <jengelh@xxxxxxxxxxxxxxx> Date: Wed Jan 2 18:22:16 2008 +0100 [NETFILTER]: xt_length match, revision 1 Introduce xt_length match revision 1. It adds support for layer4 and layer5 length matching. Signed-off-by: Jan Engelhardt <jengelh@xxxxxxxxxxxxxxx> include/linux/netfilter/xt_length.h | 18 +++ net/netfilter/xt_length.c | 196 +++++++++++++++++++++++++-- 2 files changed, 199 insertions(+), 15 deletions(-) diff --git a/include/linux/netfilter/xt_length.h b/include/linux/netfilter/xt_length.h index 7c2b439..ac4eee6 100644 --- a/include/linux/netfilter/xt_length.h +++ b/include/linux/netfilter/xt_length.h @@ -6,4 +6,22 @@ struct xt_length_info { u_int8_t invert; }; +enum { + XT_LENGTH_INVERT = 1 << 0, + + /* IP header plus payload */ + XT_LENGTH_LAYER3 = 1 << 3, + + /* TCP/UDP/etc. header plus payload */ + XT_LENGTH_LAYER4 = 1 << 4, + + /* TCP/UDP/etc. payload */ + XT_LENGTH_LAYER5 = 1 << 5, +}; + +struct xt_length_mtinfo1 { + u_int32_t min, max; + u_int16_t flags; +}; + #endif /*_XT_LENGTH_H*/ diff --git a/net/netfilter/xt_length.c b/net/netfilter/xt_length.c index ea54578..97ad627 100644 --- a/net/netfilter/xt_length.c +++ b/net/netfilter/xt_length.c @@ -1,18 +1,32 @@ -/* Kernel module to match packet length. */ -/* (C) 1999-2001 James Morris <jmorros@xxxxxxxxxxxxxxxx> +/* + * xt_length - Netfilter module to match packet length * - * 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. + * (C) 1999-2001 James Morris <jmorros@xxxxxxxxxxxxxxxx> + * Copyright © CC Computer Consultants GmbH, 2007-2008 + * Jan Engelhardt <jengelh@xxxxxxxxxxxxxxx> + * + * 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 <linux/module.h> #include <linux/skbuff.h> +#include <linux/icmp.h> +#include <linux/ip.h> #include <linux/ipv6.h> +#include <linux/tcp.h> +#include <linux/udp.h> #include <net/ip.h> - -#include <linux/netfilter/xt_length.h> +#include <net/ipv6.h> #include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_length.h> +#include <linux/netfilter_ipv6/ip6_tables.h> +#ifndef NEXTHDR_IPV4 +# define NEXTHDR_IPV4 4 +#endif +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +# define WITH_IPV6 1 +#endif MODULE_AUTHOR("James Morris <jmorris@xxxxxxxxxxxxxxxx>"); MODULE_DESCRIPTION("IP tables packet length matching module"); @@ -21,45 +35,197 @@ MODULE_ALIAS("ipt_length"); MODULE_ALIAS("ip6t_length"); static bool +length_mt_v0(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_length_info *info = matchinfo; + u_int16_t pktlen = ntohs(ip_hdr(skb)->tot_len); + + return (pktlen >= info->min && pktlen <= info->max) ^ info->invert; +} + +static bool xtlength_layer5_tcp(unsigned int *length, const struct sk_buff *skb, + unsigned int offset) +{ + const struct tcphdr *tcph; + struct tcphdr buf; + + tcph = skb_header_pointer(skb, offset, sizeof(buf), &buf); + if (tcph == NULL) + return false; + + *length = skb->len - offset - 4 * tcph->doff; + return true; +} + +static inline bool +xtlength_layer5(unsigned int *length, const struct sk_buff *skb, + unsigned int prot, unsigned int offset) +{ + switch (prot) { + case IPPROTO_TCP: + return xtlength_layer5_tcp(length, skb, offset); + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + *length = skb->len - offset - sizeof(struct udphdr); + return true; + case IPPROTO_ICMP: + *length = skb->len - offset - sizeof(struct icmphdr); + return true; + case IPPROTO_ICMPV6: + *length = skb->len - offset - offsetof(struct icmp6hdr, icmp6_dataun); + return true; + case IPPROTO_AH: + *length = skb->len - offset - sizeof(struct ip_auth_hdr); + return true; + case IPPROTO_ESP: + *length = skb->len - offset - sizeof(struct ip_esp_hdr); + return true; + } + return false; +} + +static bool length_mt(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_length_mtinfo1 *info = matchinfo; + const struct iphdr *iph = ip_hdr(skb); + unsigned int len = 0; + bool hit = true; + + if (info->flags & XT_LENGTH_LAYER3) + len = ntohs(iph->tot_len); + else if (info->flags & XT_LENGTH_LAYER4) + len = ntohs(iph->tot_len) - protoff; + else if (info->flags & XT_LENGTH_LAYER5) + hit = xtlength_layer5(&len, skb, iph->protocol, protoff); + if (!hit) + return false; + + return (len >= info->min && len <= info->max) ^ + !!(info->flags & XT_LENGTH_INVERT); +} + +#ifdef WITH_IPV6 +static bool +length_mt6_v0(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_length_info *info = matchinfo; - u_int16_t pktlen = ntohs(ip_hdr(skb)->tot_len); + const u_int16_t pktlen = ntohs(ipv6_hdr(skb)->payload_len) + + sizeof(struct ipv6hdr); return (pktlen >= info->min && pktlen <= info->max) ^ info->invert; } +/* + * llayer4_proto - figure out the L4 protocol in an IPv6 packet + * @skb: skb pointer + * @offset: position at which L4 starts (equal to 'protoff' in IPv4 code) + * @hotdrop: hotdrop pointer + * + * Searches for a recognized L4 header. On success, fills in @offset and + * returns the protocol number. If not found, %NEXTHDR_MAX is returned. + * On error, @hotdrop is set. + */ +static unsigned int +llayer4_proto(const struct sk_buff *skb, unsigned int *offset, bool *hotdrop) +{ + /* + * Do encapsulation first so that %NEXTHDR_TCP does not hit the TCP + * part in an IPv6-in-IPv6 encapsulation. + */ + static const unsigned int types[] = + {NEXTHDR_IPV6, NEXTHDR_IPV4, NEXTHDR_ESP, NEXTHDR_AUTH, + NEXTHDR_ICMP, NEXTHDR_TCP, NEXTHDR_UDP}; + unsigned int i; + int err; + + for (i = 0; i < ARRAY_SIZE(types); ++i) { + err = ipv6_find_hdr(skb, offset, types[i], NULL); + if (err >= 0) + return types[i]; + if (err != -ENOENT) { + *hotdrop = true; + break; + } + } + + return NEXTHDR_MAX; +} + static bool length_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_length_info *info = matchinfo; - const u_int16_t pktlen = ntohs(ipv6_hdr(skb)->payload_len) + - sizeof(struct ipv6hdr); + const struct xt_length_mtinfo1 *info = matchinfo; + const struct ipv6hdr *iph = ipv6_hdr(skb); + unsigned int len = 0, l4proto; + bool hit = true; - return (pktlen >= info->min && pktlen <= info->max) ^ info->invert; + if (info->flags & XT_LENGTH_LAYER3) { + len = sizeof(struct ipv6hdr) + ntohs(iph->payload_len); + } else { + l4proto = llayer4_proto(skb, &protoff, hotdrop); + if (l4proto == NEXTHDR_MAX) + return false; + if (info->flags & XT_LENGTH_LAYER4) + len = skb->len - protoff; + else if (info->flags & XT_LENGTH_LAYER5) + hit = xtlength_layer5(&len, skb, l4proto, protoff); + } + if (!hit) + return false; + + return (len >= info->min && len <= info->max) ^ + !!(info->flags & XT_LENGTH_INVERT); } +#endif static struct xt_match length_mt_reg[] __read_mostly = { { .name = "length", + .revision = 0, .family = AF_INET, - .match = length_mt, + .match = length_mt_v0, .matchsize = sizeof(struct xt_length_info), .me = THIS_MODULE, }, { + .name = "length", + .revision = 1, + .family = AF_INET, + .match = length_mt, + .matchsize = sizeof(struct xt_length_mtinfo1), + .me = THIS_MODULE, + }, +#ifdef WITH_IPV6 + { .name = "length", + .revision = 0, .family = AF_INET6, - .match = length_mt6, + .match = length_mt6_v0, .matchsize = sizeof(struct xt_length_info), .me = THIS_MODULE, }, + { + .name = "length", + .revision = 1, + .family = AF_INET6, + .match = length_mt6, + .matchsize = sizeof(struct xt_length_mtinfo1), + .me = THIS_MODULE, + }, +#endif }; static int __init length_mt_init(void) - To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html