From: Jan Engelhardt <jengelh@medozas.de>
To: kaber@trash.net
Cc: netfilter-devel@vger.kernel.org, xiaosuo@gmail.com
Subject: [PATCH] netfilter: xtables: introduce xt_length revision 2
Date: Sat, 24 Jul 2010 10:55:18 +0200 [thread overview]
Message-ID: <1279961719-20479-1-git-send-email-jengelh@medozas.de> (raw)
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@medozas.de>
---
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@intercode.com.au>
+ * Copyright © Jan Engelhardt <jengelh@medozas.de>, 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@intercode.com.au>");
+MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
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@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
next reply other threads:[~2010-07-24 8:55 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-07-24 8:55 Jan Engelhardt [this message]
2010-07-24 11:42 ` [PATCH] netfilter: xtables: introduce xt_length revision 2 Eric Dumazet
2010-07-24 12:27 ` [PATCH] netfilter: xtables: introduce xt_length revision 2½ Jan Engelhardt
2010-08-02 16:01 ` Patrick McHardy
2010-08-02 17:01 ` Jan Engelhardt
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1279961719-20479-1-git-send-email-jengelh@medozas.de \
--to=jengelh@medozas.de \
--cc=kaber@trash.net \
--cc=netfilter-devel@vger.kernel.org \
--cc=xiaosuo@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).