Introduce xt_length match revision 1. It adds support for layer-4, layer-5 and layer-7 length matching. It is much easier than writing up the according xt_u32 magic. This can be used for packet scheduling; specific example are online games where all data is transferred over the same port, but the regular gameplay has a characteristically lower packet size than bulk downloads of game maps. (Tested with Unreal Tournament 99.) Signed-off-by: Jan Engelhardt <jengelh@xxxxxxxxxx> --- include/linux/netfilter/xt_length.h | 18 +++ net/netfilter/xt_length.c | 230 ++++++++++++++++++++++++++++++++++- 2 files changed, 245 insertions(+), 3 deletions(-) diff --git a/include/linux/netfilter/xt_length.h b/include/linux/netfilter/xt_length.h index b82ed7c..7eb7e56 100644 --- a/include/linux/netfilter/xt_length.h +++ b/include/linux/netfilter/xt_length.h @@ -8,4 +8,22 @@ struct xt_length_info { __u8 invert; }; +enum { + XT_LENGTH_INVERT = 1 << 0, + + /* IP header plus payload */ + XT_LENGTH_LAYER3 = 1 << 1, + /* Strip IP header: */ + XT_LENGTH_LAYER4 = 1 << 2, + /* Strip TCP/UDP/etc. header */ + XT_LENGTH_LAYER5 = 1 << 3, + /* TCP/UDP/SCTP payload */ + XT_LENGTH_LAYER7 = 1 << 4, +}; + +struct xt_length_mtinfo2 { + __u32 min, max; + __u16 flags; +}; + #endif /*_XT_LENGTH_H*/ diff --git a/net/netfilter/xt_length.c b/net/netfilter/xt_length.c index 176e557..2092686 100644 --- a/net/netfilter/xt_length.c +++ b/net/netfilter/xt_length.c @@ -1,20 +1,31 @@ /* Kernel module to match packet length. */ /* (C) 1999-2001 James Morris <jmorros@xxxxxxxxxxxxxxxx> + * Copyright © Jan Engelhardt <jengelh@xxxxxxxxxx>, 2007 - 2010 * * 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/dccp.h> #include <linux/module.h> #include <linux/skbuff.h> +#include <linux/icmp.h> +#include <linux/ip.h> #include <linux/ipv6.h> +#include <linux/sctp.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> +#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) +# define WITH_IPV6 1 +#endif MODULE_AUTHOR("James Morris <jmorris@xxxxxxxxxxxxxxxx>"); +MODULE_AUTHOR("Jan Engelhardt <jengelh@xxxxxxxxxx>"); MODULE_DESCRIPTION("Xtables: Packet length (Layer3,4,5) match"); MODULE_LICENSE("GPL"); MODULE_ALIAS("ipt_length"); @@ -39,6 +50,201 @@ length_mt6(const struct sk_buff *skb, struct xt_action_param *par) 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; + if (*length >= 4 * tcph->doff) + *length -= 4 * tcph->doff; + return true; +} + +static bool +xtlength_layer5_dccp(unsigned int *length, const struct sk_buff *skb, + unsigned int offset) +{ + const struct dccp_hdr *dh; + struct dccp_hdr dhbuf; + + dh = skb_header_pointer(skb, offset, sizeof(dhbuf), &dhbuf); + if (dh == NULL) + return false; + + *length = skb->len - offset; + if (*length >= 4 * dh->dccph_doff) + *length -= 4 * dh->dccph_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_SCTP: + *length = skb->len - offset - sizeof(struct sctphdr); + return true; + case IPPROTO_DCCP: + return xtlength_layer5_dccp(length, skb, offset); + 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 +xtlength_layer7_sctp(unsigned int *length, const struct sk_buff *skb, + unsigned int offset) +{ + const struct sctp_chunkhdr *ch; + struct sctp_chunkhdr chbuf; + unsigned int pos; + + *length = 0; + for (pos = sizeof(struct sctphdr); pos < skb->len; + pos += ntohs(ch->length)) + { + ch = skb_header_pointer(skb, offset + pos, + sizeof(chbuf), &chbuf); + if (ch == NULL) + return false; + if (ch->type != SCTP_CID_DATA) + continue; + *length += ntohs(ch->length); + } + return true; +} + +static bool xtlength_layer7(unsigned int *length, const struct sk_buff *skb, + unsigned int proto, unsigned int offset) +{ + switch (proto) { + case IPPROTO_SCTP: + return xtlength_layer7_sctp(length, skb, offset); + default: + return xtlength_layer5(length, skb, proto, offset); + } +} + +static bool +length2_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_length_mtinfo2 *info = par->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) - par->thoff; + else if (info->flags & XT_LENGTH_LAYER5) + hit = xtlength_layer5(&len, skb, iph->protocol, par->thoff); + else if (info->flags & XT_LENGTH_LAYER7) + hit = xtlength_layer7(&len, skb, iph->protocol, par->thoff); + if (!hit) + return false; + + return (len >= info->min && len <= info->max) ^ + !!(info->flags & XT_LENGTH_INVERT); +} + +#ifdef WITH_IPV6 +/** + * 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[] = + {IPPROTO_IPV6, IPPROTO_IPIP, IPPROTO_ESP, IPPROTO_AH, + IPPROTO_ICMP, IPPROTO_TCP, IPPROTO_UDP, IPPROTO_UDPLITE, + IPPROTO_SCTP, IPPROTO_DCCP}; + 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 +length2_mt6(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_length_mtinfo2 *info = par->matchinfo; + const struct ipv6hdr *iph = ipv6_hdr(skb); + unsigned int len = 0, l4proto; + unsigned int thoff = par->thoff; + bool hit = true; + + if (info->flags & XT_LENGTH_LAYER3) { + if (iph->payload_len == 0) + /* Jumbograms */ + len = skb->len; + else + len = sizeof(struct ipv6hdr) + ntohs(iph->payload_len); + } else { + l4proto = llayer4_proto(skb, &thoff, &par->hotdrop); + if (l4proto == NEXTHDR_MAX) + return false; + if (info->flags & XT_LENGTH_LAYER4) + len = skb->len - thoff; + else if (info->flags & XT_LENGTH_LAYER5) + hit = xtlength_layer5(&len, skb, l4proto, thoff); + else if (info->flags & XT_LENGTH_LAYER7) + hit = xtlength_layer7(&len, skb, l4proto, thoff); + } + 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", @@ -54,6 +260,24 @@ static struct xt_match length_mt_reg[] __read_mostly = { .matchsize = sizeof(struct xt_length_info), .me = THIS_MODULE, }, + { + .name = "length2", + .revision = 2, + .family = NFPROTO_IPV4, + .match = length2_mt, + .matchsize = sizeof(struct xt_length_mtinfo2), + .me = THIS_MODULE, + }, +#ifdef WITH_IPV6 + { + .name = "length2", + .revision = 2, + .family = NFPROTO_IPV6, + .match = length2_mt6, + .matchsize = sizeof(struct xt_length_mtinfo2), + .me = THIS_MODULE, + }, +#endif }; static int __init length_mt_init(void) -- 1.7.1 -- 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