All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] xt_u32 - match arbitrary bits and bytes of a packet
@ 2007-06-02 21:46 Jan Engelhardt
  2007-06-02 21:50   ` Jan Engelhardt
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Jan Engelhardt @ 2007-06-02 21:46 UTC (permalink / raw)
  To: Netfilter Mailing List, Netfilter Developer Mailing List,
	Linux Kernel Mailing List

Hello!


along comes xt_u32, a revamped ipt_u32,

    *	added ipv6 support since that seemed dead simple, given u32's 
	task. I would have even liked to unlock u32 for _all_ protocols, 
	but .family = AF_UNSPEC does not do the right thing right now, 
	but that's not so much a showstopper.

	And arptables seems miles away from using iptables modules. So
	AF_INET and AF_INET6 it is for now.

    *	Reduced the buffer size to 17 KB. I think that is quite ok since 
	I added an overflow check,  SHOULD THERE BE ANY device with an 
	MTU larger than our loopback masterpiece (16436 bytes).

	Are there such devices that support Megasuperjumboframes?
	The previous buffer size of 64 KB was probably the cutting edge,
	as a single IPv4 fragment/packet does not support more than that 
	anyway.


Questions, comments, blame, praise, please.
I'd like to get this merged so I do not have to maintain it out-of-tree.


	Jan
-- 


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 1/2] xt_u32 (kernel) - match arbitrary bits and bytes of a packet
  2007-06-02 21:46 [PATCH 0/2] xt_u32 - match arbitrary bits and bytes of a packet Jan Engelhardt
@ 2007-06-02 21:50   ` Jan Engelhardt
  2007-06-02 21:51 ` [PATCH 2/2] xt_u32 (iptables) " Jan Engelhardt
  2007-06-03  5:07 ` [PATCH 0/2] xt_u32 " Valdis.Kletnieks
  2 siblings, 0 replies; 11+ messages in thread
From: Jan Engelhardt @ 2007-06-02 21:50 UTC (permalink / raw)
  To: Netfilter Mailing List, Netfilter Developer Mailing List,
	Linux Kernel Mailing List


Adds the U32 module that has been sitting in POM-NG for ages.
Additionally, more features:

along comes xt_u32, a revamped ipt_u32,

    *	added ipv6 support since that seemed dead simple, given u32's
	task. I would have even liked to unlock u32 for _all_ protocols,
	but .family = AF_UNSPEC does not do the right thing right now,  
	but that's not so much a showstopper.

	And arptables seems miles away from using iptables modules. So
	AF_INET and AF_INET6 it is for now.

    *	Reduced the buffer size to 17 KB. I think that is quite ok since
	I added an overflow check,  SHOULD THERE BE ANY device with an  
	MTU larger than our loopback masterpiece (16436 bytes).

	Are there such devices that support Megasuperjumboframes?
	The previous buffer size of 64 KB was probably the cutting edge,
	as a single IPv4 fragment/packet does not support more than that
	anyway.


Signed-off-by: Jan Engelhardt <jengelh@gmx.de>

---
 include/linux/netfilter/xt_u32.h |   37 ++++++
 net/netfilter/Kconfig            |   13 ++
 net/netfilter/Makefile           |    1 
 net/netfilter/xt_u32.c           |  234 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 285 insertions(+)

Index: linux-2.6.22-rc3-git6/include/linux/netfilter/xt_u32.h
===================================================================
--- /dev/null
+++ linux-2.6.22-rc3-git6/include/linux/netfilter/xt_u32.h
@@ -0,0 +1,37 @@
+#ifndef _XT_U32_H
+#define _XT_U32_H 1
+
+enum xt_u32_ops {
+	XT_U32_AND,
+	XT_U32_LEFTSH,
+	XT_U32_RIGHTSH,
+	XT_U32_AT,
+};
+
+struct xt_u32_location_element {
+	uint32_t number;
+	uint8_t nextop;
+};
+
+struct xt_u32_value_element {
+	uint32_t min, max;
+};
+
+/*
+ * Any way to allow for an arbitrary number of elements?
+ * For now, I settle with a limit of 10 each.
+ */
+#define XT_U32_MAXSIZE 10
+
+struct xt_u32_test {
+	struct xt_u32_location_element location[XT_U32_MAXSIZE+1];
+	struct xt_u32_value_element value[XT_U32_MAXSIZE+1];
+	uint8_t nnums, nvalues;
+};
+
+struct xt_u32 {
+	struct xt_u32_test tests[XT_U32_MAXSIZE+1];
+	uint8_t ntests;
+};
+
+#endif /* _XT_U32_H */
Index: linux-2.6.22-rc3-git6/net/netfilter/Kconfig
===================================================================
--- linux-2.6.22-rc3-git6.orig/net/netfilter/Kconfig
+++ linux-2.6.22-rc3-git6/net/netfilter/Kconfig
@@ -644,6 +644,19 @@ config NETFILTER_XT_MATCH_TCPMSS
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
+config NETFILTER_XT_MATCH_U32
+	tristate '"u32" match support'
+	depends on NETFILTER_XTABLES
+	---help---
+	  u32 allows you to extract quantities of up to 4 bytes from a packet,
+	  AND them with specified masks, shift them by specified amounts and
+	  test whether the results are in any of a set of specified ranges.
+	  The specification of what to extract is general enough to skip over
+	  headers with lengths stored in the packet, as in IP or TCP header
+	  lengths.
+
+	  Details and examples are in the kernel module source.
+
 config NETFILTER_XT_MATCH_HASHLIMIT
 	tristate '"hashlimit" match support'
 	depends on NETFILTER_XTABLES && (IP6_NF_IPTABLES || IP6_NF_IPTABLES=n)
Index: linux-2.6.22-rc3-git6/net/netfilter/Makefile
===================================================================
--- linux-2.6.22-rc3-git6.orig/net/netfilter/Makefile
+++ linux-2.6.22-rc3-git6/net/netfilter/Makefile
@@ -73,4 +73,5 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_STATISTI
 obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_U32) += xt_u32.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_HASHLIMIT) += xt_hashlimit.o
Index: linux-2.6.22-rc3-git6/net/netfilter/xt_u32.c
===================================================================
--- /dev/null
+++ linux-2.6.22-rc3-git6/net/netfilter/xt_u32.c
@@ -0,0 +1,234 @@
+/*
+ *	xt_u32 - kernel module to match u32 packet content
+ *
+ *	Original author: Don Cohen <don@isis.cs3-inc.com>
+ *	© Jan Engelhardt <jengelh@gmx.de>, 2007
+ */
+
+/*
+U32 tests whether quantities of up to 4 bytes extracted from a packet
+have specified values.  The specification of what to extract is general
+enough to find data at given offsets from tcp headers or payloads.
+
+ --u32 tests
+ The argument amounts to a program in a small language described below.
+ tests := location = value |  tests && location = value
+ value := range | value , range
+ range := number | number : number
+  a single number, n, is interpreted the same as n:n
+  n:m is interpreted as the range of numbers >=n and <=m
+ location := number | location operator number
+ operator := & | << | >> | @
+
+ The operators &, <<, >>, && mean the same as in c.  The = is really a set
+ membership operator and the value syntax describes a set.  The @ operator
+ is what allows moving to the next header and is described further below.
+
+ *** Until I can find out how to avoid it, there are some artificial limits
+ on the size of the tests:
+ - no more than 10 ='s (and 9 &&'s) in the u32 argument
+ - no more than 10 ranges (and 9 commas) per value
+ - no more than 10 numbers (and 9 operators) per location
+
+ To describe the meaning of location, imagine the following machine that
+ interprets it.  There are three registers:
+  A is of type char*, initially the address of the IP header
+  B and C are unsigned 32 bit integers, initially zero
+
+  The instructions are:
+   number	B = number;
+                C = (*(A+B)<<24)+(*(A+B+1)<<16)+(*(A+B+2)<<8)+*(A+B+3)
+   &number	C = C&number
+   <<number	C = C<<number
+   >>number	C = C>>number
+   @number	A = A+C; then do the instruction number
+  Any access of memory outside [skb->head,skb->end] causes the match to fail.
+  Otherwise the result of the computation is the final value of C.
+
+ Whitespace is allowed but not required in the tests.
+ However the characters that do occur there are likely to require
+ shell quoting, so it's a good idea to enclose the arguments in quotes.
+
+Example:
+ match IP packets with total length >= 256
+ The IP header contains a total length field in bytes 2-3.
+ --u32 "0&0xFFFF=0x100:0xFFFF"
+ read bytes 0-3
+ AND that with FFFF (giving bytes 2-3),
+ and test whether that's in the range [0x100:0xFFFF]
+
+Example: (more realistic, hence more complicated)
+ match icmp packets with icmp type 0
+ First test that it's an icmp packet, true iff byte 9 (protocol) = 1
+ --u32 "6&0xFF=1 && ...
+ read bytes 6-9, use & to throw away bytes 6-8 and compare the result to 1
+ Next test that it's not a fragment.
+  (If so it might be part of such a packet but we can't always tell.)
+  n.b. This test is generally needed if you want to match anything
+  beyond the IP header.
+ The last 6 bits of byte 6 and all of byte 7 are 0 iff this is a complete
+ packet (not a fragment).  Alternatively, you can allow first fragments
+ by only testing the last 5 bits of byte 6.
+ ... 4&0x3FFF=0 && ...
+ Last test: the first byte past the IP header (the type) is 0
+ This is where we have to use the @syntax.  The length of the IP header
+ (IHL) in 32 bit words is stored in the right half of byte 0 of the
+ IP header itself.
+ ... 0>>22&0x3C@0>>24=0"
+ The first 0 means read bytes 0-3,
+ >>22 means shift that 22 bits to the right.  Shifting 24 bits would give
+   the first byte, so only 22 bits is four times that plus a few more bits.
+ &3C then eliminates the two extra bits on the right and the first four
+ bits of the first byte.
+ For instance, if IHL=5 then the IP header is 20 (4 x 5) bytes long.
+ In this case bytes 0-1 are (in binary) xxxx0101 yyzzzzzz,
+ >>22 gives the 10 bit value xxxx0101yy and &3C gives 010100.
+ @ means to use this number as a new offset into the packet, and read
+ four bytes starting from there.  This is the first 4 bytes of the icmp
+ payload, of which byte 0 is the icmp type.  Therefore we simply shift
+ the value 24 to the right to throw out all but the first byte and compare
+ the result with 0.
+
+Example:
+ tcp payload bytes 8-12 is any of 1, 2, 5 or 8
+ First we test that the packet is a tcp packet (similar to icmp).
+ --u32 "6&0xFF=6 && ...
+ Next, test that it's not a fragment (same as above).
+ ... 0>>22&0x3C@12>>26&0x3C@8=1,2,5,8"
+ 0>>22&3C as above computes the number of bytes in the IP header.
+ @ makes this the new offset into the packet, which is the start of the
+ tcp header.  The length of the tcp header (again in 32 bit words) is
+ the left half of byte 12 of the tcp header.  The 12>>26&3C
+ computes this length in bytes (similar to the IP header before).
+ @ makes this the new offset, which is the start of the tcp payload.
+ Finally 8 reads bytes 8-12 of the payload and = checks whether the
+ result is any of 1, 2, 5 or 8
+*/
+
+#include <linux/module.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>
+
+/* This is slow, but it's simple. --RR */
+
+/*
+ * I think 17KB should suffice. The largest MTU I have
+ * seen so far is lo's, being 16436. -jengelh
+ */
+static char xt_u32_buffer[17*1024];
+static DEFINE_SPINLOCK(xt_u32_lock);
+
+static int xt_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, int *hotdrop)
+{
+	const struct xt_u32 *data = matchinfo;
+	const struct xt_u32_test *ct;
+	const unsigned char *base, *head;
+	int i, nnums, nvals, testind;
+	uint32_t pos, val, at;
+
+	spin_lock_bh(&xt_u32_lock);
+
+	head = skb_header_pointer(skb, 0, min(skb->len,
+	       sizeof(xt_u32_buffer)), xt_u32_buffer);
+	if (head == NULL) {
+		*hotdrop = 1;
+		return false;
+	}
+
+	base = head;
+	for (testind = 0; testind < data->ntests; ++testind) {
+		ct = &data->tests[testind];
+
+		at  = 0;
+		pos = ct->location[0].number;
+		if (at + pos + 3 > skb->len || at + pos < 0) {
+			spin_unlock_bh(&xt_u32_lock);
+			return false;
+		}
+
+		val = (base[pos] << 24) | (base[pos+1] << 16) |
+		      (base[pos+2] << 8) | base[pos+3];
+		nnums = ct->nnums;
+
+		for (i = 1; i < nnums; ++i) {
+			uint32_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:
+				at += val;
+				pos = number;
+				if (at + pos + 3 > skb->len || at + pos < 0) {
+					spin_unlock_bh(&xt_u32_lock);
+					return 0;
+				}
+
+				val = (base[at+pos] << 24) |
+				      (base[at+pos+1] << 16) |
+				      (base[at+pos+2] << 8) | base[at+pos+3];
+				break;
+			}
+		}
+
+		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) {
+			spin_unlock_bh(&xt_u32_lock);
+			return false;
+		}
+	}
+
+	spin_unlock_bh(&xt_u32_lock);
+	return 1;
+}
+
+static struct xt_match xt_u32_reg[] = {
+	{
+		.name       = "u32",
+		.family     = AF_INET,
+		.match      = xt_u32_match,
+		.matchsize  = sizeof(struct xt_u32),
+		.me         = THIS_MODULE,
+	},
+	{
+		.name       = "u32",
+		.family     = AF_INET6,
+		.match      = xt_u32_match,
+		.matchsize  = sizeof(struct xt_u32),
+		.me         = THIS_MODULE,
+	},
+};
+
+static int __init xt_u32_init(void)
+{
+	return xt_register_matches(xt_u32_reg, ARRAY_SIZE(xt_u32_reg));
+}
+
+static void __exit xt_u32_exit(void)
+{
+	xt_unregister_matches(xt_u32_reg, ARRAY_SIZE(xt_u32_reg));
+	return;
+}
+
+module_init(xt_u32_init);
+module_exit(xt_u32_exit);
+MODULE_AUTHOR("Don Cohen <don@isis.cs3-inc.com>");
+MODULE_DESCRIPTION("netfilter u32 match module");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_u32");


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 1/2] xt_u32 (kernel) - match arbitrary bits and bytes of a packet
@ 2007-06-02 21:50   ` Jan Engelhardt
  0 siblings, 0 replies; 11+ messages in thread
From: Jan Engelhardt @ 2007-06-02 21:50 UTC (permalink / raw)
  To: Netfilter Mailing List, Netfilter Developer Mailing List,
	Linux Kernel Mailing List


Adds the U32 module that has been sitting in POM-NG for ages.
Additionally, more features:

along comes xt_u32, a revamped ipt_u32,

    *	added ipv6 support since that seemed dead simple, given u32's
	task. I would have even liked to unlock u32 for _all_ protocols,
	but .family = AF_UNSPEC does not do the right thing right now,  
	but that's not so much a showstopper.

	And arptables seems miles away from using iptables modules. So
	AF_INET and AF_INET6 it is for now.

    *	Reduced the buffer size to 17 KB. I think that is quite ok since
	I added an overflow check,  SHOULD THERE BE ANY device with an  
	MTU larger than our loopback masterpiece (16436 bytes).

	Are there such devices that support Megasuperjumboframes?
	The previous buffer size of 64 KB was probably the cutting edge,
	as a single IPv4 fragment/packet does not support more than that
	anyway.


Signed-off-by: Jan Engelhardt <jengelh@gmx.de>

---
 include/linux/netfilter/xt_u32.h |   37 ++++++
 net/netfilter/Kconfig            |   13 ++
 net/netfilter/Makefile           |    1 
 net/netfilter/xt_u32.c           |  234 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 285 insertions(+)

Index: linux-2.6.22-rc3-git6/include/linux/netfilter/xt_u32.h
===================================================================
--- /dev/null
+++ linux-2.6.22-rc3-git6/include/linux/netfilter/xt_u32.h
@@ -0,0 +1,37 @@
+#ifndef _XT_U32_H
+#define _XT_U32_H 1
+
+enum xt_u32_ops {
+	XT_U32_AND,
+	XT_U32_LEFTSH,
+	XT_U32_RIGHTSH,
+	XT_U32_AT,
+};
+
+struct xt_u32_location_element {
+	uint32_t number;
+	uint8_t nextop;
+};
+
+struct xt_u32_value_element {
+	uint32_t min, max;
+};
+
+/*
+ * Any way to allow for an arbitrary number of elements?
+ * For now, I settle with a limit of 10 each.
+ */
+#define XT_U32_MAXSIZE 10
+
+struct xt_u32_test {
+	struct xt_u32_location_element location[XT_U32_MAXSIZE+1];
+	struct xt_u32_value_element value[XT_U32_MAXSIZE+1];
+	uint8_t nnums, nvalues;
+};
+
+struct xt_u32 {
+	struct xt_u32_test tests[XT_U32_MAXSIZE+1];
+	uint8_t ntests;
+};
+
+#endif /* _XT_U32_H */
Index: linux-2.6.22-rc3-git6/net/netfilter/Kconfig
===================================================================
--- linux-2.6.22-rc3-git6.orig/net/netfilter/Kconfig
+++ linux-2.6.22-rc3-git6/net/netfilter/Kconfig
@@ -644,6 +644,19 @@ config NETFILTER_XT_MATCH_TCPMSS
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
+config NETFILTER_XT_MATCH_U32
+	tristate '"u32" match support'
+	depends on NETFILTER_XTABLES
+	---help---
+	  u32 allows you to extract quantities of up to 4 bytes from a packet,
+	  AND them with specified masks, shift them by specified amounts and
+	  test whether the results are in any of a set of specified ranges.
+	  The specification of what to extract is general enough to skip over
+	  headers with lengths stored in the packet, as in IP or TCP header
+	  lengths.
+
+	  Details and examples are in the kernel module source.
+
 config NETFILTER_XT_MATCH_HASHLIMIT
 	tristate '"hashlimit" match support'
 	depends on NETFILTER_XTABLES && (IP6_NF_IPTABLES || IP6_NF_IPTABLES=n)
Index: linux-2.6.22-rc3-git6/net/netfilter/Makefile
===================================================================
--- linux-2.6.22-rc3-git6.orig/net/netfilter/Makefile
+++ linux-2.6.22-rc3-git6/net/netfilter/Makefile
@@ -73,4 +73,5 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_STATISTI
 obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_U32) += xt_u32.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_HASHLIMIT) += xt_hashlimit.o
Index: linux-2.6.22-rc3-git6/net/netfilter/xt_u32.c
===================================================================
--- /dev/null
+++ linux-2.6.22-rc3-git6/net/netfilter/xt_u32.c
@@ -0,0 +1,234 @@
+/*
+ *	xt_u32 - kernel module to match u32 packet content
+ *
+ *	Original author: Don Cohen <don@isis.cs3-inc.com>
+ *	© Jan Engelhardt <jengelh@gmx.de>, 2007
+ */
+
+/*
+U32 tests whether quantities of up to 4 bytes extracted from a packet
+have specified values.  The specification of what to extract is general
+enough to find data at given offsets from tcp headers or payloads.
+
+ --u32 tests
+ The argument amounts to a program in a small language described below.
+ tests := location = value |  tests && location = value
+ value := range | value , range
+ range := number | number : number
+  a single number, n, is interpreted the same as n:n
+  n:m is interpreted as the range of numbers >=n and <=m
+ location := number | location operator number
+ operator := & | << | >> | @
+
+ The operators &, <<, >>, && mean the same as in c.  The = is really a set
+ membership operator and the value syntax describes a set.  The @ operator
+ is what allows moving to the next header and is described further below.
+
+ *** Until I can find out how to avoid it, there are some artificial limits
+ on the size of the tests:
+ - no more than 10 ='s (and 9 &&'s) in the u32 argument
+ - no more than 10 ranges (and 9 commas) per value
+ - no more than 10 numbers (and 9 operators) per location
+
+ To describe the meaning of location, imagine the following machine that
+ interprets it.  There are three registers:
+  A is of type char*, initially the address of the IP header
+  B and C are unsigned 32 bit integers, initially zero
+
+  The instructions are:
+   number	B = number;
+                C = (*(A+B)<<24)+(*(A+B+1)<<16)+(*(A+B+2)<<8)+*(A+B+3)
+   &number	C = C&number
+   <<number	C = C<<number
+   >>number	C = C>>number
+   @number	A = A+C; then do the instruction number
+  Any access of memory outside [skb->head,skb->end] causes the match to fail.
+  Otherwise the result of the computation is the final value of C.
+
+ Whitespace is allowed but not required in the tests.
+ However the characters that do occur there are likely to require
+ shell quoting, so it's a good idea to enclose the arguments in quotes.
+
+Example:
+ match IP packets with total length >= 256
+ The IP header contains a total length field in bytes 2-3.
+ --u32 "0&0xFFFF=0x100:0xFFFF"
+ read bytes 0-3
+ AND that with FFFF (giving bytes 2-3),
+ and test whether that's in the range [0x100:0xFFFF]
+
+Example: (more realistic, hence more complicated)
+ match icmp packets with icmp type 0
+ First test that it's an icmp packet, true iff byte 9 (protocol) = 1
+ --u32 "6&0xFF=1 && ...
+ read bytes 6-9, use & to throw away bytes 6-8 and compare the result to 1
+ Next test that it's not a fragment.
+  (If so it might be part of such a packet but we can't always tell.)
+  n.b. This test is generally needed if you want to match anything
+  beyond the IP header.
+ The last 6 bits of byte 6 and all of byte 7 are 0 iff this is a complete
+ packet (not a fragment).  Alternatively, you can allow first fragments
+ by only testing the last 5 bits of byte 6.
+ ... 4&0x3FFF=0 && ...
+ Last test: the first byte past the IP header (the type) is 0
+ This is where we have to use the @syntax.  The length of the IP header
+ (IHL) in 32 bit words is stored in the right half of byte 0 of the
+ IP header itself.
+ ... 0>>22&0x3C@0>>24=0"
+ The first 0 means read bytes 0-3,
+ >>22 means shift that 22 bits to the right.  Shifting 24 bits would give
+   the first byte, so only 22 bits is four times that plus a few more bits.
+ &3C then eliminates the two extra bits on the right and the first four
+ bits of the first byte.
+ For instance, if IHL=5 then the IP header is 20 (4 x 5) bytes long.
+ In this case bytes 0-1 are (in binary) xxxx0101 yyzzzzzz,
+ >>22 gives the 10 bit value xxxx0101yy and &3C gives 010100.
+ @ means to use this number as a new offset into the packet, and read
+ four bytes starting from there.  This is the first 4 bytes of the icmp
+ payload, of which byte 0 is the icmp type.  Therefore we simply shift
+ the value 24 to the right to throw out all but the first byte and compare
+ the result with 0.
+
+Example:
+ tcp payload bytes 8-12 is any of 1, 2, 5 or 8
+ First we test that the packet is a tcp packet (similar to icmp).
+ --u32 "6&0xFF=6 && ...
+ Next, test that it's not a fragment (same as above).
+ ... 0>>22&0x3C@12>>26&0x3C@8=1,2,5,8"
+ 0>>22&3C as above computes the number of bytes in the IP header.
+ @ makes this the new offset into the packet, which is the start of the
+ tcp header.  The length of the tcp header (again in 32 bit words) is
+ the left half of byte 12 of the tcp header.  The 12>>26&3C
+ computes this length in bytes (similar to the IP header before).
+ @ makes this the new offset, which is the start of the tcp payload.
+ Finally 8 reads bytes 8-12 of the payload and = checks whether the
+ result is any of 1, 2, 5 or 8
+*/
+
+#include <linux/module.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>
+
+/* This is slow, but it's simple. --RR */
+
+/*
+ * I think 17KB should suffice. The largest MTU I have
+ * seen so far is lo's, being 16436. -jengelh
+ */
+static char xt_u32_buffer[17*1024];
+static DEFINE_SPINLOCK(xt_u32_lock);
+
+static int xt_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, int *hotdrop)
+{
+	const struct xt_u32 *data = matchinfo;
+	const struct xt_u32_test *ct;
+	const unsigned char *base, *head;
+	int i, nnums, nvals, testind;
+	uint32_t pos, val, at;
+
+	spin_lock_bh(&xt_u32_lock);
+
+	head = skb_header_pointer(skb, 0, min(skb->len,
+	       sizeof(xt_u32_buffer)), xt_u32_buffer);
+	if (head == NULL) {
+		*hotdrop = 1;
+		return false;
+	}
+
+	base = head;
+	for (testind = 0; testind < data->ntests; ++testind) {
+		ct = &data->tests[testind];
+
+		at  = 0;
+		pos = ct->location[0].number;
+		if (at + pos + 3 > skb->len || at + pos < 0) {
+			spin_unlock_bh(&xt_u32_lock);
+			return false;
+		}
+
+		val = (base[pos] << 24) | (base[pos+1] << 16) |
+		      (base[pos+2] << 8) | base[pos+3];
+		nnums = ct->nnums;
+
+		for (i = 1; i < nnums; ++i) {
+			uint32_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:
+				at += val;
+				pos = number;
+				if (at + pos + 3 > skb->len || at + pos < 0) {
+					spin_unlock_bh(&xt_u32_lock);
+					return 0;
+				}
+
+				val = (base[at+pos] << 24) |
+				      (base[at+pos+1] << 16) |
+				      (base[at+pos+2] << 8) | base[at+pos+3];
+				break;
+			}
+		}
+
+		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) {
+			spin_unlock_bh(&xt_u32_lock);
+			return false;
+		}
+	}
+
+	spin_unlock_bh(&xt_u32_lock);
+	return 1;
+}
+
+static struct xt_match xt_u32_reg[] = {
+	{
+		.name       = "u32",
+		.family     = AF_INET,
+		.match      = xt_u32_match,
+		.matchsize  = sizeof(struct xt_u32),
+		.me         = THIS_MODULE,
+	},
+	{
+		.name       = "u32",
+		.family     = AF_INET6,
+		.match      = xt_u32_match,
+		.matchsize  = sizeof(struct xt_u32),
+		.me         = THIS_MODULE,
+	},
+};
+
+static int __init xt_u32_init(void)
+{
+	return xt_register_matches(xt_u32_reg, ARRAY_SIZE(xt_u32_reg));
+}
+
+static void __exit xt_u32_exit(void)
+{
+	xt_unregister_matches(xt_u32_reg, ARRAY_SIZE(xt_u32_reg));
+	return;
+}
+
+module_init(xt_u32_init);
+module_exit(xt_u32_exit);
+MODULE_AUTHOR("Don Cohen <don@isis.cs3-inc.com>");
+MODULE_DESCRIPTION("netfilter u32 match module");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_u32");

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 2/2] xt_u32 (iptables) - match arbitrary bits and bytes of a packet
  2007-06-02 21:46 [PATCH 0/2] xt_u32 - match arbitrary bits and bytes of a packet Jan Engelhardt
  2007-06-02 21:50   ` Jan Engelhardt
@ 2007-06-02 21:51 ` Jan Engelhardt
  2007-06-03  5:07 ` [PATCH 0/2] xt_u32 " Valdis.Kletnieks
  2 siblings, 0 replies; 11+ messages in thread
From: Jan Engelhardt @ 2007-06-02 21:51 UTC (permalink / raw)
  To: Netfilter Mailing List, Netfilter Developer Mailing List,
	Linux Kernel Mailing List


Signed-off-by: Jan Engelhardt <jengelh@gmx.de>

---
 extensions/.u32-test      |    2 
 extensions/libipt_u32.c   |  290 ++++++++++++++++++++++++++++++++++++++++++++++
 extensions/libipt_u32.man |    8 +
 3 files changed, 300 insertions(+)

Index: iptables/extensions/.u32-test
===================================================================
--- /dev/null
+++ iptables/extensions/.u32-test
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f "$KERNEL_DIR/include/linux/netfilter/xt_u32.h" ] && echo u32
Index: iptables/extensions/libipt_u32.c
===================================================================
--- /dev/null
+++ iptables/extensions/libipt_u32.c
@@ -0,0 +1,290 @@
+/* Shared library add-on to iptables to add u32 matching,
+ * generalized matching on values found at packet offsets
+ *
+ * Detailed doc is in the kernel module source
+ * net/netfilter/xt_u32.c
+ *
+ * (C) 2002 by Don Cohen <don-netf@isis.cs3-inc.com>
+ * Released under the terms of GNU GPL v2
+ */
+#include <sys/types.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <iptables.h>
+#include <linux/netfilter/xt_u32.h>
+
+/* Function which prints out usage message. */
+static void help(void)
+{
+	printf( "u32 v%s options:\n"
+		" --u32 tests\n"
+		" tests := location = value | tests && location = value\n"
+		" value := range | value , range\n"
+		" range := number | number : number\n"
+		" location := number | location operator number\n"
+		" operator := & | << | >> | @\n",
+		IPTABLES_VERSION);
+}
+
+/* defined in /usr/include/getopt.h maybe in man getopt */
+static struct option opts[] = {
+	{"u32", 1, NULL, '1'},
+	{NULL},
+};
+
+/* shared printing code */
+static void print_u32(const struct xt_u32 *data)
+{
+	const struct xt_u32_test *ct;
+	unsigned int testind, i;
+
+	for (testind = 0; testind < data->ntests; ++testind) {
+		ct = &data->tests[testind];
+
+		if (testind > 0)
+			printf("&&");
+
+		printf("0x%x", ct->location[0].number);
+		for (i = 1; i < ct->nnums; ++i) {
+			switch (ct->location[i].nextop) {
+			case XT_U32_AND:
+				printf("&");
+				break;
+			case XT_U32_LEFTSH:
+				printf("<<");
+				break;
+			case XT_U32_RIGHTSH:
+				printf(">>");
+				break;
+			case XT_U32_AT:
+				printf("@");
+				break;
+			}
+			printf("0x%x", ct->location[i].number);
+		}
+
+		printf("=");
+		for (i = 0; i < ct->nvalues; ++i) {
+			if (i > 0)
+				printf(",");
+			if (ct->value[i].min == ct->value[i].max)
+				printf("0x%x", ct->value[i].min);
+			else
+				printf("0x%x:0x%x", ct->value[i].min,
+				       ct->value[i].max);
+		}
+	}
+	printf(" ");
+}
+
+/* string_to_number is not quite what we need here ... */
+uint32_t parse_number(char **s, int pos)
+{
+	uint32_t number;
+	char *end;
+	errno = 0;
+
+	number = strtoul(*s, &end, 0);
+	if (end == *s)
+		exit_error(PARAMETER_PROBLEM,
+			   "u32: at char %d: expected number", pos);
+	if (errno)
+		exit_error(PARAMETER_PROBLEM,
+			   "u32: at char %d: error reading number", pos);
+	*s = end;
+	return number;
+}
+
+/* Function which parses command options; returns true if it ate an option */
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+		 const struct ipt_entry *entry, unsigned int *nfcache,
+		 struct ipt_entry_match **match)
+{
+	struct xt_u32 *data = (void *)(*match)->data;
+	char *arg = argv[optind-1]; /* the argument string */
+	char *start = arg;
+	int state = 0, testind = 0, locind = 0, valind = 0;
+
+	if (c != '1')
+		return 0;
+
+	/*
+	 * states:
+	 * 0 = looking for numbers and operations,
+	 * 1 = looking for ranges
+	 */
+	while (1) {
+		/* read next operand/number or range */
+		while (isspace(*arg))
+			++arg;
+
+		if (*arg == '\0') {
+			/* end of argument found */
+			if (state == 0)
+				exit_error(PARAMETER_PROBLEM,
+					   "u32: input ended in location spec");
+			if (valind == 0)
+				exit_error(PARAMETER_PROBLEM,
+					   "u32: test ended with no value spec");
+
+			data->tests[testind].nnums   = locind;
+			data->tests[testind].nvalues = valind;
+			testind++;
+			data->ntests = testind;
+
+			if (testind > XT_U32_MAXSIZE)
+				exit_error(PARAMETER_PROBLEM,
+				           "u32: at char %d: too many \"&&\"s",
+				           arg - start);
+			return 1;
+		}
+
+		if (state == 0) {
+			/*
+			 * reading location: read a number if nothing read yet,
+			 * otherwise either op number or = to end location spec
+			 */
+			if (*arg == '=') {
+				if (locind == 0) {
+					exit_error(PARAMETER_PROBLEM,
+					           "u32: at char %d: "
+					           "location spec missing",
+					           arg - start);
+				} else {
+					arg++;
+					state = 1;
+				}
+			} else {
+				if (locind != 0) {
+					/* need op before number */
+					if (*arg == '&') {
+						data->tests[testind].location[locind].nextop = XT_U32_AND;
+					} else if (*arg == '<') {
+						arg++;
+						if (*arg != '<')
+							exit_error(PARAMETER_PROBLEM,
+								   "u32: at char %d: a second < expected", arg - start);
+						data->tests[testind].location[locind].nextop = XT_U32_LEFTSH;
+					} else if (*arg == '>') {
+						arg++;
+						if (*arg != '>')
+							exit_error(PARAMETER_PROBLEM,
+								   "u32: at char %d: a second > expected", arg - start);
+						data->tests[testind].location[locind].nextop = XT_U32_RIGHTSH;
+					} else if (*arg == '@') {
+						data->tests[testind].location[locind].nextop = XT_U32_AT;
+					} else {
+						exit_error(PARAMETER_PROBLEM,
+							"u32: at char %d: operator expected", arg - start);
+					}
+					++arg;
+				}
+				/* now a number; string_to_number skips white space? */
+				data->tests[testind].location[locind].number =
+					parse_number(&arg, arg - start);
+				locind++;
+				if (locind > XT_U32_MAXSIZE)
+					exit_error(PARAMETER_PROBLEM,
+						   "u32: at char %d: too many operators", arg - start);
+			}
+		} else {
+			/*
+			 * state 1 - reading values: read a range if nothing
+			 * read yet, otherwise either ,range or && to end
+			 * test spec
+			 */
+			if (*arg == '&') {
+				arg++;
+				if (*arg != '&')
+					exit_error(PARAMETER_PROBLEM,
+						   "u32: at char %d: a second & was expected", arg - start);
+				if (valind == 0) {
+					exit_error(PARAMETER_PROBLEM,
+						   "u32: at char %d: value spec missing", arg - start);
+				} else {
+					data->tests[testind].nnums   = locind;
+					data->tests[testind].nvalues = valind;
+					++testind;
+					if (testind > XT_U32_MAXSIZE)
+						exit_error(PARAMETER_PROBLEM,
+							   "u32: at char %d: too many \"&&\"s", arg - start);
+					++arg;
+					state  = 0;
+					locind = 0;
+					valind = 0;
+				}
+			}
+			else { /* read value range */
+				if (valind) { /* need , before number */
+					if (*arg != ',')
+						exit_error(PARAMETER_PROBLEM,
+							   "u32: at char %d: expected , or &&", arg - start);
+					arg++;
+				}
+				data->tests[testind].value[valind].min = parse_number(&arg, arg - start);
+
+				while (isspace(*arg))
+					++arg;
+
+				if (*arg==':') {
+					arg++;
+					data->tests[testind].value[valind].max =
+						parse_number(&arg, arg-start);
+				} else {
+					data->tests[testind].value[valind].max =
+						data->tests[testind].value[valind].min;
+				}
+
+				valind++;
+				if (valind > XT_U32_MAXSIZE)
+					exit_error(PARAMETER_PROBLEM,
+						   "u32: at char %d: too many ,'s", arg-start);
+			}
+		}
+	}
+}
+
+/* Final check; must specify something. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Prints out the matchinfo. */
+static void print(const struct ipt_ip *ip, const struct ipt_entry_match *match,
+	          int numeric)
+{
+	printf("u32 ");
+	print_u32((const void *)match->data);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+	printf("--u32 ");
+	print_u32((const void *)match->data);
+}
+
+struct iptables_match u32 = {
+	.next		= NULL,
+	.name		= "u32",
+	.version	= IPTABLES_VERSION,
+	.size		= IPT_ALIGN(sizeof(struct xt_u32)),
+	.userspacesize	= IPT_ALIGN(sizeof(struct xt_u32)),
+	.help		= &help,
+	.parse		= &parse,
+	.final_check	= &final_check,
+	.print		= &print,
+	.save		= &save,
+	.extra_opts	= opts,
+};
+
+static __attribute__((constructor)) void libipt_u32_init(void)
+{
+	register_match(&u32);
+}
Index: iptables/extensions/libipt_u32.man
===================================================================
--- /dev/null
+++ iptables/extensions/libipt_u32.man
@@ -0,0 +1,8 @@
+U32 allows you to extract quantities of up to 4 bytes from a packet,
+AND them with specified masks, shift them by specified amounts and
+test whether the results are in any of a set of specified ranges.
+The specification of what to extract is general enough to skip over
+headers with lengths stored in the packet, as in IP or TCP header
+lengths.
+
+Details and examples are in the kernel module source.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 0/2] xt_u32 - match arbitrary bits and bytes of a packet
  2007-06-02 21:46 [PATCH 0/2] xt_u32 - match arbitrary bits and bytes of a packet Jan Engelhardt
  2007-06-02 21:50   ` Jan Engelhardt
  2007-06-02 21:51 ` [PATCH 2/2] xt_u32 (iptables) " Jan Engelhardt
@ 2007-06-03  5:07 ` Valdis.Kletnieks
  2007-06-03  8:20   ` Jan Engelhardt
  2 siblings, 1 reply; 11+ messages in thread
From: Valdis.Kletnieks @ 2007-06-03  5:07 UTC (permalink / raw)
  To: Jan Engelhardt
  Cc: Netfilter Mailing List, Netfilter Developer Mailing List,
	Linux Kernel Mailing List

[-- Attachment #1: Type: text/plain, Size: 665 bytes --]

On Sat, 02 Jun 2007 23:46:46 +0200, Jan Engelhardt said:

> along comes xt_u32, a revamped ipt_u32,

+1 for doing this - I've been dragging along a local ipt_u32 patch for a while,
and been wishing it had ipv6 support.

>     *	Reduced the buffer size to 17 KB. I think that is quite ok since 
> 	I added an overflow check,  SHOULD THERE BE ANY device with an 
> 	MTU larger than our loopback masterpiece (16436 bytes).
> 
> 	Are there such devices that support Megasuperjumboframes?

IP over Infiniband? I think those have some outrageous MTU?  Personally,
I wouldn't need it for anything bigger than 9K ethernet jumbograms, but
that one's a decision for others.


[-- Attachment #2: Type: application/pgp-signature, Size: 226 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 0/2] xt_u32 - match arbitrary bits and bytes of a packet
  2007-06-03  5:07 ` [PATCH 0/2] xt_u32 " Valdis.Kletnieks
@ 2007-06-03  8:20   ` Jan Engelhardt
  0 siblings, 0 replies; 11+ messages in thread
From: Jan Engelhardt @ 2007-06-03  8:20 UTC (permalink / raw)
  To: Valdis.Kletnieks
  Cc: Netfilter Mailing List, Netfilter Developer Mailing List,
	Linux Kernel Mailing List


On Jun 3 2007 01:07, Valdis.Kletnieks@vt.edu wrote:
>On Sat, 02 Jun 2007 23:46:46 +0200, Jan Engelhardt said:
>
>> along comes xt_u32, a revamped ipt_u32,
>
>+1 for doing this - I've been dragging along a local ipt_u32 patch for a while,
>and been wishing it had ipv6 support.
>
>>     *	Reduced the buffer size to 17 KB. I think that is quite ok since 
>> 	I added an overflow check,  SHOULD THERE BE ANY device with an 
>> 	MTU larger than our loopback masterpiece (16436 bytes).
>> 
>> 	Are there such devices that support Megasuperjumboframes?
>
>IP over Infiniband? I think those have some outrageous MTU?  Personally,
>I wouldn't need it for anything bigger than 9K ethernet jumbograms, but
>that one's a decision for others.

Can you find out? For such supersize packets, I'd like to switch to
kmalloc/vmalloc rather than keeping a big buffer in the .bss.


	Jan
-- 

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/2] xt_u32 (kernel) - match arbitrary bits and bytes of a packet
  2007-06-02 21:50   ` Jan Engelhardt
  (?)
@ 2007-06-03 17:23   ` Patrick McHardy
  2007-06-03 20:09     ` Jan Engelhardt
  2007-06-05  7:07     ` Jan Engelhardt
  -1 siblings, 2 replies; 11+ messages in thread
From: Patrick McHardy @ 2007-06-03 17:23 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

[Please don't flood unrelated mailinglists with netfilter patches,
 this includes the netfilter user list]

Jan Engelhardt wrote:
>     *	added ipv6 support since that seemed dead simple, given u32's
> 	task. I would have even liked to unlock u32 for _all_ protocols,
> 	but .family = AF_UNSPEC does not do the right thing right now,  
> 	but that's not so much a showstopper.
> 
> 	And arptables seems miles away from using iptables modules. So
> 	AF_INET and AF_INET6 it is for now.


arp_tables doesn't support matches at all.

> 
>     *	Reduced the buffer size to 17 KB. I think that is quite ok since
> 	I added an overflow check,  SHOULD THERE BE ANY device with an  
> 	MTU larger than our loopback masterpiece (16436 bytes).
> 
> 	Are there such devices that support Megasuperjumboframes?
> 	The previous buffer size of 64 KB was probably the cutting edge,
> 	as a single IPv4 fragment/packet does not support more than that
> 	anyway.

Think of TSO.

> 
> 
> Signed-off-by: Jan Engelhardt <jengelh@gmx.de>
> 
> ---
>  include/linux/netfilter/xt_u32.h |   37 ++++++
>  net/netfilter/Kconfig            |   13 ++
>  net/netfilter/Makefile           |    1 
>  net/netfilter/xt_u32.c           |  234 +++++++++++++++++++++++++++++++++++++++
>  4 files changed, 285 insertions(+)
> 
> Index: linux-2.6.22-rc3-git6/include/linux/netfilter/xt_u32.h
> ===================================================================
> --- /dev/null
> +++ linux-2.6.22-rc3-git6/include/linux/netfilter/xt_u32.h
> +struct xt_u32_value_element {
> +	uint32_t min, max;

We use u_int32_t in all netfilter files. Also

	u_int32_t min;
	u_int32_t max;

please (and everywhere else of course).

> +#endif /* _XT_U32_H */
> Index: linux-2.6.22-rc3-git6/net/netfilter/Kconfig
> ===================================================================
> --- linux-2.6.22-rc3-git6.orig/net/netfilter/Kconfig
> +++ linux-2.6.22-rc3-git6/net/netfilter/Kconfig
> @@ -644,6 +644,19 @@ config NETFILTER_XT_MATCH_TCPMSS
>  
>  	  To compile it as a module, choose M here.  If unsure, say N.
>  
> +config NETFILTER_XT_MATCH_U32
> +	tristate '"u32" match support'
> +	depends on NETFILTER_XTABLES
> +	---help---
> +	  u32 allows you to extract quantities of up to 4 bytes from a packet,
> +	  AND them with specified masks, shift them by specified amounts and
> +	  test whether the results are in any of a set of specified ranges.
> +	  The specification of what to extract is general enough to skip over
> +	  headers with lengths stored in the packet, as in IP or TCP header
> +	  lengths.
> +
> +	  Details and examples are in the kernel module source.

Details and examples belong in the manpage.

> +++ linux-2.6.22-rc3-git6/net/netfilter/xt_u32.c
> @@ -0,0 +1,234 @@
> +/*
> + *	xt_u32 - kernel module to match u32 packet content
> + *
> + *	Original author: Don Cohen <don@isis.cs3-inc.com>
> + *	© Jan Engelhardt <jengelh@gmx.de>, 2007
> + */
> +
> +/*
> +U32 tests whether quantities of up to 4 bytes extracted from a packet
> +have specified values.  The specification of what to extract is general
> +enough to find data at given offsets from tcp headers or payloads.
> +
> + --u32 tests
> + The argument amounts to a program in a small language described below.
> + tests := location = value |  tests && location = value
> + value := range | value , range
> + range := number | number : number
> +  a single number, n, is interpreted the same as n:n
> +  n:m is interpreted as the range of numbers >=n and <=m
> + location := number | location operator number
> + operator := & | << | >> | @
> +
> + The operators &, <<, >>, && mean the same as in c.  The = is really a set
> + membership operator and the value syntax describes a set.  The @ operator
> + is what allows moving to the next header and is described further below.
> +
> + *** Until I can find out how to avoid it, there are some artificial limits
> + on the size of the tests:
> + - no more than 10 ='s (and 9 &&'s) in the u32 argument
> + - no more than 10 ranges (and 9 commas) per value
> + - no more than 10 numbers (and 9 operators) per location
> +
> + To describe the meaning of location, imagine the following machine that
> + interprets it.  There are three registers:
> +  A is of type char*, initially the address of the IP header
> +  B and C are unsigned 32 bit integers, initially zero
> +
> +  The instructions are:
> +   number	B = number;
> +                C = (*(A+B)<<24)+(*(A+B+1)<<16)+(*(A+B+2)<<8)+*(A+B+3)
> +   &number	C = C&number
> +   <<number	C = C<<number
> +   >>number	C = C>>number
> +   @number	A = A+C; then do the instruction number
> +  Any access of memory outside [skb->head,skb->end] causes the match to fail.
> +  Otherwise the result of the computation is the final value of C.
> +
> + Whitespace is allowed but not required in the tests.
> + However the characters that do occur there are likely to require
> + shell quoting, so it's a good idea to enclose the arguments in quotes.
> +
> +Example:
> + match IP packets with total length >= 256
> + The IP header contains a total length field in bytes 2-3.
> + --u32 "0&0xFFFF=0x100:0xFFFF"
> + read bytes 0-3
> + AND that with FFFF (giving bytes 2-3),
> + and test whether that's in the range [0x100:0xFFFF]
> +
> +Example: (more realistic, hence more complicated)
> + match icmp packets with icmp type 0
> + First test that it's an icmp packet, true iff byte 9 (protocol) = 1
> + --u32 "6&0xFF=1 && ...
> + read bytes 6-9, use & to throw away bytes 6-8 and compare the result to 1
> + Next test that it's not a fragment.
> +  (If so it might be part of such a packet but we can't always tell.)
> +  n.b. This test is generally needed if you want to match anything
> +  beyond the IP header.
> + The last 6 bits of byte 6 and all of byte 7 are 0 iff this is a complete
> + packet (not a fragment).  Alternatively, you can allow first fragments
> + by only testing the last 5 bits of byte 6.
> + ... 4&0x3FFF=0 && ...
> + Last test: the first byte past the IP header (the type) is 0
> + This is where we have to use the @syntax.  The length of the IP header
> + (IHL) in 32 bit words is stored in the right half of byte 0 of the
> + IP header itself.
> + ... 0>>22&0x3C@0>>24=0"
> + The first 0 means read bytes 0-3,
> + >>22 means shift that 22 bits to the right.  Shifting 24 bits would give
> +   the first byte, so only 22 bits is four times that plus a few more bits.
> + &3C then eliminates the two extra bits on the right and the first four
> + bits of the first byte.
> + For instance, if IHL=5 then the IP header is 20 (4 x 5) bytes long.
> + In this case bytes 0-1 are (in binary) xxxx0101 yyzzzzzz,
> + >>22 gives the 10 bit value xxxx0101yy and &3C gives 010100.
> + @ means to use this number as a new offset into the packet, and read
> + four bytes starting from there.  This is the first 4 bytes of the icmp
> + payload, of which byte 0 is the icmp type.  Therefore we simply shift
> + the value 24 to the right to throw out all but the first byte and compare
> + the result with 0.
> +
> +Example:
> + tcp payload bytes 8-12 is any of 1, 2, 5 or 8
> + First we test that the packet is a tcp packet (similar to icmp).
> + --u32 "6&0xFF=6 && ...
> + Next, test that it's not a fragment (same as above).
> + ... 0>>22&0x3C@12>>26&0x3C@8=1,2,5,8"
> + 0>>22&3C as above computes the number of bytes in the IP header.
> + @ makes this the new offset into the packet, which is the start of the
> + tcp header.  The length of the tcp header (again in 32 bit words) is
> + the left half of byte 12 of the tcp header.  The 12>>26&3C
> + computes this length in bytes (similar to the IP header before).
> + @ makes this the new offset, which is the start of the tcp payload.
> + Finally 8 reads bytes 8-12 of the payload and = checks whether the
> + result is any of 1, 2, 5 or 8
> +*/

Remove all the above up to the copyright please.

> +
> +#include <linux/module.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>
> +
> +/* This is slow, but it's simple. --RR */
> +
> +/*
> + * I think 17KB should suffice. The largest MTU I have
> + * seen so far is lo's, being 16436. -jengelh
> + */
> +static char xt_u32_buffer[17*1024];


64k and please allocate this.

> +static DEFINE_SPINLOCK(xt_u32_lock);
> +
> +static int xt_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, int *hotdrop)
> +{
> +	const struct xt_u32 *data = matchinfo;
> +	const struct xt_u32_test *ct;
> +	const unsigned char *base, *head;
> +	int i, nnums, nvals, testind;
> +	uint32_t pos, val, at;
> +
> +	spin_lock_bh(&xt_u32_lock);
> +
> +	head = skb_header_pointer(skb, 0, min(skb->len,
> +	       sizeof(xt_u32_buffer)), xt_u32_buffer);

min can go with 64k buffer.

> +	if (head == NULL) {
> +		*hotdrop = 1;
> +		return false;
> +	}

might as well BUG_ON since a copy of size <= skb->len cant fail.

> +
> +	base = head;
> +	for (testind = 0; testind < data->ntests; ++testind) {
> +		ct = &data->tests[testind];
> +
> +		at  = 0;
> +		pos = ct->location[0].number;
> +		if (at + pos + 3 > skb->len || at + pos < 0) {
> +			spin_unlock_bh(&xt_u32_lock);
> +			return false;

what about inversion? Matches return int, so please use 0/1
(or send me a patch to convert all of them to boolean first).

> +		}
> +
> +		val = (base[pos] << 24) | (base[pos+1] << 16) |
> +		      (base[pos+2] << 8) | base[pos+3];
> +		nnums = ct->nnums;
> +
> +		for (i = 1; i < nnums; ++i) {
> +			uint32_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:
> +				at += val;
> +				pos = number;
> +				if (at + pos + 3 > skb->len || at + pos < 0) {
> +					spin_unlock_bh(&xt_u32_lock);
> +					return 0;
> +				}
> +
> +				val = (base[at+pos] << 24) |
> +				      (base[at+pos+1] << 16) |
> +				      (base[at+pos+2] << 8) | base[at+pos+3];
> +				break;
> +			}
> +		}
> +
> +		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) {
> +			spin_unlock_bh(&xt_u32_lock);
> +			return false;
> +		}
> +	}
> +
> +	spin_unlock_bh(&xt_u32_lock);
> +	return 1;
> +}
> +
> +static struct xt_match xt_u32_reg[] = {
> +	{
> +		.name       = "u32",
> +		.family     = AF_INET,
> +		.match      = xt_u32_match,
> +		.matchsize  = sizeof(struct xt_u32),
> +		.me         = THIS_MODULE,
> +	},
> +	{
> +		.name       = "u32",
> +		.family     = AF_INET6,
> +		.match      = xt_u32_match,
> +		.matchsize  = sizeof(struct xt_u32),
> +		.me         = THIS_MODULE,
> +	},
> +};
> +
> +static int __init xt_u32_init(void)
> +{
> +	return xt_register_matches(xt_u32_reg, ARRAY_SIZE(xt_u32_reg));
> +}
> +
> +static void __exit xt_u32_exit(void)
> +{
> +	xt_unregister_matches(xt_u32_reg, ARRAY_SIZE(xt_u32_reg));
> +	return;
> +}
> +
> +module_init(xt_u32_init);
> +module_exit(xt_u32_exit);
> +MODULE_AUTHOR("Don Cohen <don@isis.cs3-inc.com>");
> +MODULE_DESCRIPTION("netfilter u32 match module");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("ipt_u32");
> 

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/2] xt_u32 (kernel) - match arbitrary bits and bytes of a packet
  2007-06-03 17:23   ` Patrick McHardy
@ 2007-06-03 20:09     ` Jan Engelhardt
  2007-06-04 11:25       ` Patrick McHardy
  2007-06-05  7:07     ` Jan Engelhardt
  1 sibling, 1 reply; 11+ messages in thread
From: Jan Engelhardt @ 2007-06-03 20:09 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Jun 3 2007 19:23, Patrick McHardy wrote:
>
>Jan Engelhardt wrote:
>>     *	added ipv6 support since that seemed dead simple, given u32's
>> 	task. I would have even liked to unlock u32 for _all_ protocols,
>> 	but .family = AF_UNSPEC does not do the right thing right now,  
>> 	but that's not so much a showstopper.
>> 
>> 	And arptables seems miles away from using iptables modules. So
>> 	AF_INET and AF_INET6 it is for now.
>
>arp_tables doesn't support matches at all.

Oh well, so much for that idea.

>> 
>>     *	Reduced the buffer size to 17 KB. I think that is quite ok since
>> 	I added an overflow check,  SHOULD THERE BE ANY device with an  
>> 	MTU larger than our loopback masterpiece (16436 bytes).
>> 
>> 	Are there such devices that support Megasuperjumboframes?
>> 	The previous buffer size of 64 KB was probably the cutting edge,
>> 	as a single IPv4 fragment/packet does not support more than that
>> 	anyway.
>
>Think of TSO.

Right, next version already uses kmalloc.

>> +	uint32_t min, max;
>
>We use u_int32_t in all netfilter files. Also
>
>	u_int32_t min;
>	u_int32_t max;
>
>please (and everywhere else of course).

Is this a showstopper? After all, uint32_t is close to the same-named 
C99 type. It's kinda strange to have __u32, u32, u_int32_t and uint32_t 
for one and the same thing.

>> +	  Details and examples are in the kernel module source.
>
>Details and examples belong in the manpage.

I agree.

>> +static char xt_u32_buffer[17*1024];
>64k and please allocate this.

Yup.

>> +	if (head == NULL) {
>> +		*hotdrop = 1;
>> +		return false;
>> +	}
>
>might as well BUG_ON since a copy of size <= skb->len cant fail.

Hmm, scripts/checkpatch.pl barfs on BUG_ONs :-)
Use WARN_ON + hotdrop, or still go with BUG_ON?

>> +	base = head;
>> +	for (testind = 0; testind < data->ntests; ++testind) {
>> +		ct = &data->tests[testind];
>> +
>> +		at  = 0;
>> +		pos = ct->location[0].number;
>> +		if (at + pos + 3 > skb->len || at + pos < 0) {
>> +			spin_unlock_bh(&xt_u32_lock);
>> +			return false;
>
>what about inversion?

If it was not supported before, I have not implemented it.

>Matches return int, so please use 0/1
>(or send me a patch to convert all of them to boolean first).

Alright!



Thanks,
	Jan
-- 

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/2] xt_u32 (kernel) - match arbitrary bits and bytes of a packet
  2007-06-03 20:09     ` Jan Engelhardt
@ 2007-06-04 11:25       ` Patrick McHardy
  0 siblings, 0 replies; 11+ messages in thread
From: Patrick McHardy @ 2007-06-04 11:25 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Jun 3 2007 19:23, Patrick McHardy wrote:
> 
>>>+	uint32_t min, max;
>>
>>We use u_int32_t in all netfilter files. Also
>>
> 
> Is this a showstopper? After all, uint32_t is close to the same-named 
> C99 type. It's kinda strange to have __u32, u32, u_int32_t and uint32_t 
> for one and the same thing.


We try to keep things consistent at least along subsystems, so yes,
I won't apply a patch that adds to the inconsistency.

>>>+	if (head == NULL) {
>>>+		*hotdrop = 1;
>>>+		return false;
>>>+	}
>>
>>might as well BUG_ON since a copy of size <= skb->len cant fail.
> 
> 
> Hmm, scripts/checkpatch.pl barfs on BUG_ONs :-)
> Use WARN_ON + hotdrop, or still go with BUG_ON?


BUG_ON please, this is what we use for all other impossible
skb_copy_bits failures.

>>>+	base = head;
>>>+	for (testind = 0; testind < data->ntests; ++testind) {
>>>+		ct = &data->tests[testind];
>>>+
>>>+		at  = 0;
>>>+		pos = ct->location[0].number;
>>>+		if (at + pos + 3 > skb->len || at + pos < 0) {
>>>+			spin_unlock_bh(&xt_u32_lock);
>>>+			return false;
>>
>>what about inversion?
> 
> 
> If it was not supported before, I have not implemented it.


We don't add new matches without inversion anymore, so please add
support for it.

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/2] xt_u32 (kernel) - match arbitrary bits and bytes of a packet
  2007-06-03 17:23   ` Patrick McHardy
  2007-06-03 20:09     ` Jan Engelhardt
@ 2007-06-05  7:07     ` Jan Engelhardt
  2007-06-05 11:34       ` Patrick McHardy
  1 sibling, 1 reply; 11+ messages in thread
From: Jan Engelhardt @ 2007-06-05  7:07 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List

Hi,

On Jun 3 2007 19:23, Patrick McHardy wrote:
>Also
>
>	u_int32_t min;
>	u_int32_t max;
>
>please (and everywhere else of course).

Might get a bit verbose.

Question:
ipt_u32/xt_u32 uses a spinlock_bh to protect the buffer to which a packet
is copied to before analyzing.

>> +	spin_lock_bh(&xt_u32_lock);
>> +
>> +	head = skb_header_pointer(skb, 0, min(skb->len,
>> +	       sizeof(xt_u32_buffer)), xt_u32_buffer);

I'd like to hear opinions about:

 * open coding skb_header_pointer

   In the "hlen - offset >= len" case in skb_header_pointer(), it just
   returns a pointer to the existing skb without copying, in which
   case we would not need to take a lock at all.

 * use percpu buffers, with or without opencoding skb_header_pointer
   so that cpus/threads do not have to wait for another -m u32 to finish.


	Jan
-- 

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/2] xt_u32 (kernel) - match arbitrary bits and bytes of a packet
  2007-06-05  7:07     ` Jan Engelhardt
@ 2007-06-05 11:34       ` Patrick McHardy
  0 siblings, 0 replies; 11+ messages in thread
From: Patrick McHardy @ 2007-06-05 11:34 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
>>>+	spin_lock_bh(&xt_u32_lock);
>>>+
>>>+	head = skb_header_pointer(skb, 0, min(skb->len,
>>>+	       sizeof(xt_u32_buffer)), xt_u32_buffer);
> 
> 
> I'd like to hear opinions about:
> 
>  * open coding skb_header_pointer
> 
>    In the "hlen - offset >= len" case in skb_header_pointer(), it just
>    returns a pointer to the existing skb without copying, in which
>    case we would not need to take a lock at all.


No special-case solutions please, this affects many netfilter modules
and we want this for all or none.

>  * use percpu buffers, with or without opencoding skb_header_pointer
>    so that cpus/threads do not have to wait for another -m u32 to finish.


Not worth the (potentially enormous) memory waste I guess.

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2007-06-05 11:34 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-06-02 21:46 [PATCH 0/2] xt_u32 - match arbitrary bits and bytes of a packet Jan Engelhardt
2007-06-02 21:50 ` [PATCH 1/2] xt_u32 (kernel) " Jan Engelhardt
2007-06-02 21:50   ` Jan Engelhardt
2007-06-03 17:23   ` Patrick McHardy
2007-06-03 20:09     ` Jan Engelhardt
2007-06-04 11:25       ` Patrick McHardy
2007-06-05  7:07     ` Jan Engelhardt
2007-06-05 11:34       ` Patrick McHardy
2007-06-02 21:51 ` [PATCH 2/2] xt_u32 (iptables) " Jan Engelhardt
2007-06-03  5:07 ` [PATCH 0/2] xt_u32 " Valdis.Kletnieks
2007-06-03  8:20   ` Jan Engelhardt

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.