]>
Commit | Line | Data |
---|---|---|
133b4e72 | 1 | include/linux/netfilter_ipv4/ipt_u32.h | 40 +++++ |
2 | net/ipv4/netfilter/Kconfig | 13 + | |
3 | net/ipv4/netfilter/Makefile | 1 | |
4 | net/ipv4/netfilter/ipt_u32.c | 236 +++++++++++++++++++++++++++++++++ | |
5 | 4 files changed, 290 insertions(+) | |
6 | ||
7 | diff -Nur --exclude '*.orig' linux.org/include/linux/netfilter_ipv4/ipt_u32.h linux/include/linux/netfilter_ipv4/ipt_u32.h | |
8 | --- linux.org/include/linux/netfilter_ipv4/ipt_u32.h 1970-01-01 00:00:00.000000000 +0000 | |
9 | +++ linux/include/linux/netfilter_ipv4/ipt_u32.h 2006-08-29 11:53:12.000000000 +0000 | |
10 | @@ -0,0 +1,40 @@ | |
11 | +#ifndef _IPT_U32_H | |
12 | +#define _IPT_U32_H | |
13 | +#include <linux/netfilter_ipv4/ip_tables.h> | |
14 | + | |
15 | +enum ipt_u32_ops | |
16 | +{ | |
17 | + IPT_U32_AND, | |
18 | + IPT_U32_LEFTSH, | |
19 | + IPT_U32_RIGHTSH, | |
20 | + IPT_U32_AT | |
21 | +}; | |
22 | + | |
23 | +struct ipt_u32_location_element | |
24 | +{ | |
25 | + u_int32_t number; | |
26 | + u_int8_t nextop; | |
27 | +}; | |
28 | +struct ipt_u32_value_element | |
29 | +{ | |
30 | + u_int32_t min; | |
31 | + u_int32_t max; | |
32 | +}; | |
33 | +/* *** any way to allow for an arbitrary number of elements? | |
34 | + for now I settle for a limit of 10 of each */ | |
35 | +#define U32MAXSIZE 10 | |
36 | +struct ipt_u32_test | |
37 | +{ | |
38 | + u_int8_t nnums; | |
39 | + struct ipt_u32_location_element location[U32MAXSIZE+1]; | |
40 | + u_int8_t nvalues; | |
41 | + struct ipt_u32_value_element value[U32MAXSIZE+1]; | |
42 | +}; | |
43 | + | |
44 | +struct ipt_u32 | |
45 | +{ | |
46 | + u_int8_t ntests; | |
47 | + struct ipt_u32_test tests[U32MAXSIZE+1]; | |
48 | +}; | |
49 | + | |
50 | +#endif /*_IPT_U32_H*/ | |
51 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/ipt_u32.c linux/net/ipv4/netfilter/ipt_u32.c | |
52 | --- linux.org/net/ipv4/netfilter/ipt_u32.c 1970-01-01 00:00:00.000000000 +0000 | |
53 | +++ linux/net/ipv4/netfilter/ipt_u32.c 2006-08-29 11:53:12.000000000 +0000 | |
54 | @@ -0,0 +1,236 @@ | |
55 | +/* Kernel module to match u32 packet content. */ | |
56 | + | |
57 | +/* | |
58 | +U32 tests whether quantities of up to 4 bytes extracted from a packet | |
59 | +have specified values. The specification of what to extract is general | |
60 | +enough to find data at given offsets from tcp headers or payloads. | |
61 | + | |
62 | + --u32 tests | |
63 | + The argument amounts to a program in a small language described below. | |
64 | + tests := location = value | tests && location = value | |
65 | + value := range | value , range | |
66 | + range := number | number : number | |
67 | + a single number, n, is interpreted the same as n:n | |
68 | + n:m is interpreted as the range of numbers >=n and <=m | |
69 | + location := number | location operator number | |
70 | + operator := & | << | >> | @ | |
71 | + | |
72 | + The operators &, <<, >>, && mean the same as in c. The = is really a set | |
73 | + membership operator and the value syntax describes a set. The @ operator | |
74 | + is what allows moving to the next header and is described further below. | |
75 | + | |
76 | + *** Until I can find out how to avoid it, there are some artificial limits | |
77 | + on the size of the tests: | |
78 | + - no more than 10 ='s (and 9 &&'s) in the u32 argument | |
79 | + - no more than 10 ranges (and 9 commas) per value | |
80 | + - no more than 10 numbers (and 9 operators) per location | |
81 | + | |
82 | + To describe the meaning of location, imagine the following machine that | |
83 | + interprets it. There are three registers: | |
84 | + A is of type char*, initially the address of the IP header | |
85 | + B and C are unsigned 32 bit integers, initially zero | |
86 | + | |
87 | + The instructions are: | |
88 | + number B = number; | |
89 | + C = (*(A+B)<<24)+(*(A+B+1)<<16)+(*(A+B+2)<<8)+*(A+B+3) | |
90 | + &number C = C&number | |
91 | + <<number C = C<<number | |
92 | + >>number C = C>>number | |
93 | + @number A = A+C; then do the instruction number | |
94 | + Any access of memory outside [skb->head,skb->end] causes the match to fail. | |
95 | + Otherwise the result of the computation is the final value of C. | |
96 | + | |
97 | + Whitespace is allowed but not required in the tests. | |
98 | + However the characters that do occur there are likely to require | |
99 | + shell quoting, so it's a good idea to enclose the arguments in quotes. | |
100 | + | |
101 | +Example: | |
102 | + match IP packets with total length >= 256 | |
103 | + The IP header contains a total length field in bytes 2-3. | |
104 | + --u32 "0&0xFFFF=0x100:0xFFFF" | |
105 | + read bytes 0-3 | |
106 | + AND that with FFFF (giving bytes 2-3), | |
107 | + and test whether that's in the range [0x100:0xFFFF] | |
108 | + | |
109 | +Example: (more realistic, hence more complicated) | |
110 | + match icmp packets with icmp type 0 | |
111 | + First test that it's an icmp packet, true iff byte 9 (protocol) = 1 | |
112 | + --u32 "6&0xFF=1 && ... | |
113 | + read bytes 6-9, use & to throw away bytes 6-8 and compare the result to 1 | |
114 | + Next test that it's not a fragment. | |
115 | + (If so it might be part of such a packet but we can't always tell.) | |
116 | + n.b. This test is generally needed if you want to match anything | |
117 | + beyond the IP header. | |
118 | + The last 6 bits of byte 6 and all of byte 7 are 0 iff this is a complete | |
119 | + packet (not a fragment). Alternatively, you can allow first fragments | |
120 | + by only testing the last 5 bits of byte 6. | |
121 | + ... 4&0x3FFF=0 && ... | |
122 | + Last test: the first byte past the IP header (the type) is 0 | |
123 | + This is where we have to use the @syntax. The length of the IP header | |
124 | + (IHL) in 32 bit words is stored in the right half of byte 0 of the | |
125 | + IP header itself. | |
126 | + ... 0>>22&0x3C@0>>24=0" | |
127 | + The first 0 means read bytes 0-3, | |
128 | + >>22 means shift that 22 bits to the right. Shifting 24 bits would give | |
129 | + the first byte, so only 22 bits is four times that plus a few more bits. | |
130 | + &3C then eliminates the two extra bits on the right and the first four | |
131 | + bits of the first byte. | |
132 | + For instance, if IHL=5 then the IP header is 20 (4 x 5) bytes long. | |
133 | + In this case bytes 0-1 are (in binary) xxxx0101 yyzzzzzz, | |
134 | + >>22 gives the 10 bit value xxxx0101yy and &3C gives 010100. | |
135 | + @ means to use this number as a new offset into the packet, and read | |
136 | + four bytes starting from there. This is the first 4 bytes of the icmp | |
137 | + payload, of which byte 0 is the icmp type. Therefore we simply shift | |
138 | + the value 24 to the right to throw out all but the first byte and compare | |
139 | + the result with 0. | |
140 | + | |
141 | +Example: | |
142 | + tcp payload bytes 8-12 is any of 1, 2, 5 or 8 | |
143 | + First we test that the packet is a tcp packet (similar to icmp). | |
144 | + --u32 "6&0xFF=6 && ... | |
145 | + Next, test that it's not a fragment (same as above). | |
146 | + ... 0>>22&0x3C@12>>26&0x3C@8=1,2,5,8" | |
147 | + 0>>22&3C as above computes the number of bytes in the IP header. | |
148 | + @ makes this the new offset into the packet, which is the start of the | |
149 | + tcp header. The length of the tcp header (again in 32 bit words) is | |
150 | + the left half of byte 12 of the tcp header. The 12>>26&3C | |
151 | + computes this length in bytes (similar to the IP header before). | |
152 | + @ makes this the new offset, which is the start of the tcp payload. | |
153 | + Finally 8 reads bytes 8-12 of the payload and = checks whether the | |
154 | + result is any of 1, 2, 5 or 8 | |
155 | +*/ | |
156 | + | |
157 | +#include <linux/module.h> | |
158 | +#include <linux/skbuff.h> | |
159 | + | |
160 | +#include <linux/netfilter_ipv4/ipt_u32.h> | |
161 | +#include <linux/netfilter_ipv4/ip_tables.h> | |
162 | + | |
163 | +/* #include <asm-i386/timex.h> for timing */ | |
164 | + | |
165 | +MODULE_AUTHOR("Don Cohen <don@isis.cs3-inc.com>"); | |
166 | +MODULE_DESCRIPTION("IP tables u32 matching module"); | |
167 | +MODULE_LICENSE("GPL"); | |
168 | + | |
169 | +/* This is slow, but it's simple. --RR */ | |
170 | +static char u32_buffer[65536]; | |
171 | +static DEFINE_SPINLOCK(u32_lock); | |
172 | + | |
173 | +static int | |
174 | +match(const struct sk_buff *skb, | |
175 | + const struct net_device *in, | |
176 | + const struct net_device *out, | |
177 | + const struct xt_match *match, | |
178 | + const void *matchinfo, | |
179 | + int offset, | |
180 | + unsigned int protoff, | |
181 | + int *hotdrop) | |
182 | +{ | |
183 | + const struct ipt_u32 *data = matchinfo; | |
184 | + int testind, i; | |
185 | + unsigned char* base; | |
186 | + unsigned char* head; | |
187 | + int nnums, nvals; | |
188 | + u_int32_t pos, val; | |
189 | + | |
190 | + u_int32_t AttPos; | |
191 | + | |
192 | + spin_lock_bh(&u32_lock); | |
193 | + | |
194 | + head = skb_header_pointer(skb, 0, skb->len, u32_buffer); | |
195 | + BUG_ON(head == NULL); | |
196 | + | |
197 | + base = head; | |
198 | + /* unsigned long long cycles1, cycles2, cycles3, cycles4; | |
199 | + cycles1 = get_cycles(); */ | |
200 | + for (testind=0; testind < data->ntests; testind++) { | |
201 | + AttPos = 0; | |
202 | + pos = data->tests[testind].location[0].number; | |
203 | + if (AttPos + pos + 3 > skb->len || AttPos + pos < 0){ | |
204 | + spin_unlock_bh(&u32_lock); | |
205 | + return 0; | |
206 | + } | |
207 | + val = (base[pos]<<24) + (base[pos+1]<<16) + | |
208 | + (base[pos+2]<<8) + base[pos+3]; | |
209 | + nnums = data->tests[testind].nnums; | |
210 | + for (i=1; i < nnums; i++) { | |
211 | + u_int32_t number = data->tests[testind].location[i].number; | |
212 | + switch (data->tests[testind].location[i].nextop) { | |
213 | + case IPT_U32_AND: | |
214 | + val = val & number; | |
215 | + break; | |
216 | + case IPT_U32_LEFTSH: | |
217 | + val = val << number; | |
218 | + break; | |
219 | + case IPT_U32_RIGHTSH: | |
220 | + val = val >> number; | |
221 | + break; | |
222 | + case IPT_U32_AT: | |
223 | + AttPos += val; | |
224 | + pos = number; | |
225 | + if (AttPos + pos + 3 > skb->len || AttPos + pos < 0) { | |
226 | + spin_unlock_bh(&u32_lock); | |
227 | + return 0; | |
228 | + } | |
229 | + | |
230 | + val = (base[AttPos + pos]<<24) | |
231 | + +(base[AttPos + pos + 1]<<16) | |
232 | + +(base[AttPos + pos + 2]<<8) | |
233 | + + base[AttPos + pos + 3]; | |
234 | + break; | |
235 | + } | |
236 | + } | |
237 | + nvals = data->tests[testind].nvalues; | |
238 | + for (i=0; i < nvals; i++) { | |
239 | + if ((data->tests[testind].value[i].min <= val) && | |
240 | + (val <= data->tests[testind].value[i].max)) { | |
241 | + break; | |
242 | + } | |
243 | + } | |
244 | + if (i >= data->tests[testind].nvalues) { | |
245 | + /* cycles2 = get_cycles(); | |
246 | + printk("failed %d in %d cycles\n", testind, | |
247 | + cycles2-cycles1); */ | |
248 | + spin_unlock_bh(&u32_lock); | |
249 | + return 0; | |
250 | + } | |
251 | + } | |
252 | + /* cycles2 = get_cycles(); | |
253 | + printk("succeeded in %d cycles\n", cycles2-cycles1); */ | |
254 | + spin_unlock_bh(&u32_lock); | |
255 | + return 1; | |
256 | +} | |
257 | + | |
258 | +static int | |
259 | +checkentry(const char *tablename, | |
260 | + const void *ip, | |
261 | + const struct xt_match *match, | |
262 | + void *matchinfo, | |
263 | + unsigned int matchsize, | |
264 | + unsigned int hook_mask) | |
265 | +{ | |
266 | + if (matchsize != IPT_ALIGN(sizeof(struct ipt_u32))) | |
267 | + return 0; | |
268 | + return 1; | |
269 | +} | |
270 | + | |
271 | +static struct ipt_match u32_match = { | |
272 | + .name = "u32", | |
273 | + .match = &match, | |
274 | + .matchsize = sizeof(struct ipt_u32), | |
275 | + .checkentry = &checkentry, | |
276 | + .me = THIS_MODULE | |
277 | +}; | |
278 | + | |
279 | +static int __init init(void) | |
280 | +{ | |
281 | + return ipt_register_match(&u32_match); | |
282 | +} | |
283 | + | |
284 | +static void __exit fini(void) | |
285 | +{ | |
286 | + ipt_unregister_match(&u32_match); | |
287 | +} | |
288 | + | |
289 | +module_init(init); | |
290 | +module_exit(fini); | |
291 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/Kconfig linux/net/ipv4/netfilter/Kconfig | |
292 | --- linux.org/net/ipv4/netfilter/Kconfig 2006-06-18 01:49:35.000000000 +0000 | |
293 | +++ linux/net/ipv4/netfilter/Kconfig 2006-08-29 11:53:12.000000000 +0000 | |
294 | @@ -613,5 +613,18 @@ | |
295 | Allows altering the ARP packet payload: source and destination | |
296 | hardware and network addresses. | |
297 | ||
298 | +config IP_NF_MATCH_U32 | |
299 | + tristate 'U32 match support' | |
300 | + depends on IP_NF_IPTABLES | |
301 | + help | |
302 | + U32 allows you to extract quantities of up to 4 bytes from a packet, | |
303 | + AND them with specified masks, shift them by specified amounts and | |
304 | + test whether the results are in any of a set of specified ranges. | |
305 | + The specification of what to extract is general enough to skip over | |
306 | + headers with lengths stored in the packet, as in IP or TCP header | |
307 | + lengths. | |
308 | + | |
309 | + Details and examples are in the kernel module source. | |
310 | + | |
311 | endmenu | |
312 | ||
313 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/Makefile linux/net/ipv4/netfilter/Makefile | |
314 | --- linux.org/net/ipv4/netfilter/Makefile 2006-06-18 01:49:35.000000000 +0000 | |
315 | +++ linux/net/ipv4/netfilter/Makefile 2006-08-29 11:53:12.000000000 +0000 | |
316 | @@ -0,0 +0,1 @@ | |
317 | +obj-$(CONFIG_IP_NF_MATCH_U32) += ipt_u32.o |