]>
Commit | Line | Data |
---|---|---|
c6410bf7 | 1 | Kconfig | 14 + |
2 | Makefile | 1 | |
3 | ipt_unclean.c | 612 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | |
4 | 3 files changed, 627 insertions(+) | |
5 | ||
6 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/Kconfig linux/net/ipv4/netfilter/Kconfig | |
7 | --- linux.org/net/ipv4/netfilter/Kconfig 2006-05-02 23:38:44.000000000 +0200 | |
8 | +++ linux/net/ipv4/netfilter/Kconfig 2006-05-04 14:49:54.000000000 +0200 | |
9 | @@ -606,5 +606,19 @@ | |
10 | Allows altering the ARP packet payload: source and destination | |
11 | hardware and network addresses. | |
12 | ||
13 | +config IP_NF_MATCH_UNCLEAN | |
14 | + tristate 'Unclean match support (DANGEROUS)' | |
15 | + depends on EXPERIMENTAL && IP_NF_IPTABLES | |
16 | + help | |
17 | + Unclean packet matching matches any strange or invalid packets, by | |
18 | + looking at a series of fields in the IP, TCP, UDP and ICMP headers. | |
19 | + | |
20 | + Please note that this kind of matching is considered dangerous and | |
21 | + might harm the future compatibility of your packet filter. | |
22 | + | |
23 | + It has happened before, search on the net for ECN blackholes :( | |
24 | + | |
25 | + | |
26 | + | |
27 | endmenu | |
28 | ||
29 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/Makefile linux/net/ipv4/netfilter/Makefile | |
30 | --- linux.org/net/ipv4/netfilter/Makefile 2006-05-02 23:38:44.000000000 +0200 | |
31 | +++ linux/net/ipv4/netfilter/Makefile 2006-05-04 14:49:54.000000000 +0200 | |
32 | @@ -0,0 +0,1 @@ | |
33 | +obj-$(CONFIG_IP_NF_MATCH_UNCLEAN) += ipt_unclean.o | |
34 | diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/ipt_unclean.c linux/net/ipv4/netfilter/ipt_unclean.c | |
35 | --- linux.org/net/ipv4/netfilter/ipt_unclean.c 1970-01-01 01:00:00.000000000 +0100 | |
36 | +++ linux/net/ipv4/netfilter/ipt_unclean.c 2006-05-04 14:49:54.000000000 +0200 | |
37 | @@ -0,0 +1,612 @@ | |
38 | +/* Kernel module to match suspect packets. */ | |
39 | +#include <linux/module.h> | |
40 | +#include <linux/skbuff.h> | |
41 | +#include <linux/ip.h> | |
42 | +#include <linux/udp.h> | |
43 | +#include <linux/tcp.h> | |
44 | +#include <linux/icmp.h> | |
45 | +#include <net/checksum.h> | |
46 | +#include <net/ip.h> | |
47 | + | |
48 | +#include <linux/netfilter_ipv4/ip_tables.h> | |
49 | + | |
50 | +#define limpk(format, args...) \ | |
51 | +do { \ | |
52 | + if (net_ratelimit()) \ | |
53 | + printk("ipt_unclean: %s" format, \ | |
54 | + embedded ? "(embedded packet) " : "" , ## args); \ | |
55 | +} while(0) | |
56 | + | |
57 | +enum icmp_error_status | |
58 | +{ | |
59 | + ICMP_MAY_BE_ERROR, | |
60 | + ICMP_IS_ERROR, | |
61 | + ICMP_NOT_ERROR | |
62 | +}; | |
63 | + | |
64 | +struct icmp_info | |
65 | +{ | |
66 | + size_t min_len, max_len; | |
67 | + enum icmp_error_status err; | |
68 | + u_int8_t min_code, max_code; | |
69 | +}; | |
70 | + | |
71 | +static int | |
72 | +check_ip(const struct sk_buff *skb, unsigned int offset); | |
73 | + | |
74 | +/* ICMP-specific checks. */ | |
75 | +static int | |
76 | +check_icmp(const struct sk_buff *skb, | |
77 | + unsigned int offset, | |
78 | + unsigned int fragoff, | |
79 | + int more_frags, | |
80 | + int embedded) | |
81 | +{ | |
82 | + struct icmphdr icmph; | |
83 | + static struct icmp_info info[] | |
84 | + = { [ICMP_ECHOREPLY] | |
85 | + = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, | |
86 | + [ICMP_DEST_UNREACH] | |
87 | + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 15 }, | |
88 | + [ICMP_SOURCE_QUENCH] | |
89 | + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 0 }, | |
90 | + [ICMP_REDIRECT] | |
91 | + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 3 }, | |
92 | + [ICMP_ECHO] | |
93 | + = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, | |
94 | + /* Router advertisement. */ | |
95 | + [9] | |
96 | + = { 8, 8 + 255 * 8, ICMP_NOT_ERROR, 0, 0 }, | |
97 | + /* Router solicitation. */ | |
98 | + [10] | |
99 | + = { 8, 8, ICMP_NOT_ERROR, 0, 0 }, | |
100 | + [ICMP_TIME_EXCEEDED] | |
101 | + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 1 }, | |
102 | + [ICMP_PARAMETERPROB] | |
103 | + = { 8 + 28, 65536, ICMP_IS_ERROR, 0, 1 }, | |
104 | + [ICMP_TIMESTAMP] | |
105 | + = { 20, 20, ICMP_NOT_ERROR, 0, 0 }, | |
106 | + [ICMP_TIMESTAMPREPLY] | |
107 | + = { 20, 20, ICMP_NOT_ERROR, 0, 0 }, | |
108 | + [ICMP_INFO_REQUEST] | |
109 | + = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, | |
110 | + [ICMP_INFO_REPLY] | |
111 | + = { 8, 65536, ICMP_NOT_ERROR, 0, 0 }, | |
112 | + [ICMP_ADDRESS] | |
113 | + = { 12, 12, ICMP_NOT_ERROR, 0, 0 }, | |
114 | + [ICMP_ADDRESSREPLY] | |
115 | + = { 12, 12, ICMP_NOT_ERROR, 0, 0 } }; | |
116 | + | |
117 | + /* Can't do anything if it's a fragment. */ | |
118 | + if (fragoff) | |
119 | + return 1; | |
120 | + | |
121 | + /* CHECK: Must have whole header.. */ | |
122 | + if (skb_copy_bits(skb, offset, &icmph, sizeof(icmph)) < 0) { | |
123 | + limpk("ICMP len=%u too short\n", skb->len - offset); | |
124 | + return 0; | |
125 | + } | |
126 | + | |
127 | + /* If not embedded in an ICMP error already. */ | |
128 | + if (!embedded) { | |
129 | + /* CHECK: Truncated ICMP (even if first fragment). */ | |
130 | + if (icmph.type < sizeof(info)/sizeof(struct icmp_info) | |
131 | + && info[icmph.type].min_len != 0 | |
132 | + && skb->len - offset < info[icmph.type].min_len) { | |
133 | + limpk("ICMP type %u len %u too short\n", | |
134 | + icmph.type, skb->len - offset); | |
135 | + return 0; | |
136 | + } | |
137 | + | |
138 | + /* CHECK: Check within known error ICMPs. */ | |
139 | + if (icmph.type < sizeof(info)/sizeof(struct icmp_info) | |
140 | + && info[icmph.type].err == ICMP_IS_ERROR) { | |
141 | + /* Max IP header size = 60 */ | |
142 | + char inner[60 + 8]; | |
143 | + struct iphdr *inner_ip = (struct iphdr *)inner; | |
144 | + | |
145 | + /* CHECK: Embedded packet must be at least | |
146 | + length of iph + 8 bytes. */ | |
147 | + if (skb_copy_bits(skb, offset + sizeof(icmph), | |
148 | + inner, sizeof(struct iphdr)+8) < 0) { | |
149 | + limpk("ICMP error internal way too short\n"); | |
150 | + return 0; | |
151 | + } | |
152 | + | |
153 | + /* iphhdr may actually be longer: still need 8 | |
154 | + actual protocol bytes. */ | |
155 | + if (offset + sizeof(icmph) + inner_ip->ihl*4 + 8 | |
156 | + > skb->len) { | |
157 | + limpk("ICMP error internal too short\n"); | |
158 | + return 0; | |
159 | + } | |
160 | + if (!check_ip(skb, offset + sizeof(icmph))) | |
161 | + return 0; | |
162 | + } | |
163 | + } else { | |
164 | + /* CHECK: Can't embed ICMP unless known non-error. */ | |
165 | + if (icmph.type >= sizeof(info)/sizeof(struct icmp_info) | |
166 | + || info[icmph.type].err != ICMP_NOT_ERROR) { | |
167 | + limpk("ICMP type %u not embeddable\n", | |
168 | + icmph.type); | |
169 | + return 0; | |
170 | + } | |
171 | + } | |
172 | + | |
173 | + /* CHECK: Invalid ICMP codes. */ | |
174 | + if (icmph.type < sizeof(info)/sizeof(struct icmp_info) | |
175 | + && (icmph.code < info[icmph.type].min_code | |
176 | + || icmph.code > info[icmph.type].max_code)) { | |
177 | + limpk("ICMP type=%u code=%u\n", | |
178 | + icmph.type, icmph.code); | |
179 | + return 0; | |
180 | + } | |
181 | + | |
182 | + /* CHECK: Above maximum length. */ | |
183 | + if (icmph.type < sizeof(info)/sizeof(struct icmp_info) | |
184 | + && info[icmph.type].max_len != 0 | |
185 | + && skb->len - offset > info[icmph.type].max_len) { | |
186 | + limpk("ICMP type=%u too long: %u bytes\n", | |
187 | + icmph.type, skb->len - offset); | |
188 | + return 0; | |
189 | + } | |
190 | + | |
191 | + switch (icmph.type) { | |
192 | + case ICMP_PARAMETERPROB: { | |
193 | + /* CHECK: Problem param must be within error packet's | |
194 | + * IP header. */ | |
195 | + u_int32_t arg = ntohl(icmph.un.gateway); | |
196 | + | |
197 | + if (icmph.code == 0) { | |
198 | + /* We've already made sure it's long enough. */ | |
199 | + struct iphdr iph; | |
200 | + skb_copy_bits(skb, offset + sizeof(icmph), &iph, | |
201 | + sizeof(iph)); | |
202 | + /* Code 0 means that upper 8 bits is pointer | |
203 | + to problem. */ | |
204 | + if ((arg >> 24) >= iph.ihl*4) { | |
205 | + limpk("ICMP PARAMETERPROB ptr = %u\n", | |
206 | + ntohl(icmph.un.gateway) >> 24); | |
207 | + return 0; | |
208 | + } | |
209 | + arg &= 0x00FFFFFF; | |
210 | + } | |
211 | + | |
212 | + /* CHECK: Rest must be zero. */ | |
213 | + if (arg) { | |
214 | + limpk("ICMP PARAMETERPROB nonzero arg = %u\n", | |
215 | + arg); | |
216 | + return 0; | |
217 | + } | |
218 | + break; | |
219 | + } | |
220 | + | |
221 | + case ICMP_TIME_EXCEEDED: | |
222 | + case ICMP_SOURCE_QUENCH: | |
223 | + /* CHECK: Unused must be zero. */ | |
224 | + if (icmph.un.gateway != 0) { | |
225 | + limpk("ICMP type=%u unused = %u\n", | |
226 | + icmph.type, ntohl(icmph.un.gateway)); | |
227 | + return 0; | |
228 | + } | |
229 | + break; | |
230 | + } | |
231 | + | |
232 | + return 1; | |
233 | +} | |
234 | + | |
235 | +/* UDP-specific checks. */ | |
236 | +static int | |
237 | +check_udp(const struct sk_buff *skb, | |
238 | + unsigned int offset, | |
239 | + unsigned int fragoff, | |
240 | + int more_frags, | |
241 | + int embedded) | |
242 | +{ | |
243 | + struct udphdr udph; | |
244 | + | |
245 | + /* Can't do anything if it's a fragment. */ | |
246 | + if (fragoff) | |
247 | + return 1; | |
248 | + | |
249 | + /* CHECK: Must cover UDP header. */ | |
250 | + if (skb_copy_bits(skb, offset, &udph, sizeof(udph)) < 0) { | |
251 | + limpk("UDP len=%u too short\n", skb->len - offset); | |
252 | + return 0; | |
253 | + } | |
254 | + | |
255 | + /* CHECK: Destination port can't be zero. */ | |
256 | + if (!udph.dest) { | |
257 | + limpk("UDP zero destination port\n"); | |
258 | + return 0; | |
259 | + } | |
260 | + | |
261 | + if (!more_frags) { | |
262 | + if (!embedded) { | |
263 | + /* CHECK: UDP length must match. */ | |
264 | + if (ntohs(udph.len) != skb->len - offset) { | |
265 | + limpk("UDP len too short %u vs %u\n", | |
266 | + ntohs(udph.len), skb->len - offset); | |
267 | + return 0; | |
268 | + } | |
269 | + } else { | |
270 | + /* CHECK: UDP length be >= this truncated pkt. */ | |
271 | + if (ntohs(udph.len) < skb->len - offset) { | |
272 | + limpk("UDP len too long %u vs %u\n", | |
273 | + ntohs(udph.len), skb->len - offset); | |
274 | + return 0; | |
275 | + } | |
276 | + } | |
277 | + } else { | |
278 | + /* CHECK: UDP length must be > this frag's length. */ | |
279 | + if (ntohs(udph.len) <= skb->len - offset) { | |
280 | + limpk("UDP fragment len too short %u vs %u\n", | |
281 | + ntohs(udph.len), skb->len - offset); | |
282 | + return 0; | |
283 | + } | |
284 | + } | |
285 | + | |
286 | + return 1; | |
287 | +} | |
288 | + | |
289 | +/* TCP-specific checks. */ | |
290 | +static int | |
291 | +check_tcp(const struct sk_buff *skb, | |
292 | + unsigned int offset, | |
293 | + unsigned int fragoff, | |
294 | + int more_frags, | |
295 | + int embedded) | |
296 | +{ | |
297 | + struct tcphdr tcph; | |
298 | + unsigned char opt[15 * 4 - sizeof(struct tcphdr)]; | |
299 | + u32 tcpflags; | |
300 | + int end_of_options = 0; | |
301 | + unsigned int i, optlen; | |
302 | + | |
303 | + /* CHECK: Can't have offset=1: used to override TCP syn-checks. */ | |
304 | + /* In fact, this is caught below (offset < 516). */ | |
305 | + | |
306 | + /* Can't do anything if it's a fragment. */ | |
307 | + if (fragoff) | |
308 | + return 1; | |
309 | + | |
310 | + /* CHECK: Smaller than minimal TCP hdr. */ | |
311 | + if (skb_copy_bits(skb, offset, &tcph, sizeof(tcph)) < 0) { | |
312 | + u16 ports[2]; | |
313 | + | |
314 | + if (!embedded) { | |
315 | + limpk("Packet length %u < TCP header.\n", | |
316 | + skb->len - offset); | |
317 | + return 0; | |
318 | + } | |
319 | + | |
320 | + /* Must have ports available (datalen >= 8), from | |
321 | + check_icmp which set embedded = 1 */ | |
322 | + /* CHECK: TCP ports inside ICMP error */ | |
323 | + skb_copy_bits(skb, offset, ports, sizeof(ports)); | |
324 | + if (!ports[0] || !ports[1]) { | |
325 | + limpk("Zero TCP ports %u/%u.\n", | |
326 | + htons(ports[0]), htons(ports[1])); | |
327 | + return 0; | |
328 | + } | |
329 | + return 1; | |
330 | + } | |
331 | + | |
332 | + /* CHECK: TCP header claims tiny size. */ | |
333 | + if (tcph.doff * 4 < sizeof(tcph)) { | |
334 | + limpk("TCP header claims tiny size %u\n", tcph.doff * 4); | |
335 | + return 0; | |
336 | + } | |
337 | + | |
338 | + /* CHECK: Packet smaller than actual TCP hdr. */ | |
339 | + optlen = tcph.doff*4 - sizeof(tcph); | |
340 | + if (skb_copy_bits(skb, offset + sizeof(tcph), opt, optlen) < 0) { | |
341 | + if (!embedded) { | |
342 | + limpk("Packet length %u < actual TCP header.\n", | |
343 | + skb->len - offset); | |
344 | + return 0; | |
345 | + } else | |
346 | + return 1; | |
347 | + } | |
348 | + | |
349 | + /* CHECK: TCP ports non-zero */ | |
350 | + if (!tcph.source || !tcph.dest) { | |
351 | + limpk("Zero TCP ports %u/%u.\n", | |
352 | + htons(tcph.source), htons(tcph.dest)); | |
353 | + return 0; | |
354 | + } | |
355 | + | |
356 | + tcpflags = tcp_flag_word(&tcph); | |
357 | + | |
358 | + /* CHECK: TCP reserved bits zero. */ | |
359 | + if (tcpflags & TCP_RESERVED_BITS) { | |
360 | + limpk("TCP reserved bits not zero\n"); | |
361 | + return 0; | |
362 | + } | |
363 | + | |
364 | + tcpflags &= ~(TCP_DATA_OFFSET | TCP_FLAG_CWR | TCP_FLAG_ECE | |
365 | + | __constant_htonl(0x0000FFFF)); | |
366 | + | |
367 | + /* CHECK: TCP flags. */ | |
368 | + if (tcpflags != TCP_FLAG_SYN | |
369 | + && tcpflags != (TCP_FLAG_SYN|TCP_FLAG_ACK) | |
370 | + && tcpflags != TCP_FLAG_RST | |
371 | + && tcpflags != (TCP_FLAG_RST|TCP_FLAG_ACK) | |
372 | + && tcpflags != (TCP_FLAG_RST|TCP_FLAG_ACK|TCP_FLAG_PSH) | |
373 | + && tcpflags != (TCP_FLAG_FIN|TCP_FLAG_ACK) | |
374 | + && tcpflags != TCP_FLAG_ACK | |
375 | + && tcpflags != (TCP_FLAG_ACK|TCP_FLAG_PSH) | |
376 | + && tcpflags != (TCP_FLAG_ACK|TCP_FLAG_URG) | |
377 | + && tcpflags != (TCP_FLAG_ACK|TCP_FLAG_URG|TCP_FLAG_PSH) | |
378 | + && tcpflags != (TCP_FLAG_FIN|TCP_FLAG_ACK|TCP_FLAG_PSH) | |
379 | + && tcpflags != (TCP_FLAG_FIN|TCP_FLAG_ACK|TCP_FLAG_URG) | |
380 | + && tcpflags != (TCP_FLAG_FIN|TCP_FLAG_ACK|TCP_FLAG_URG | |
381 | + |TCP_FLAG_PSH)) { | |
382 | + limpk("TCP flags bad: 0x%04X\n", ntohl(tcpflags) >> 16); | |
383 | + return 0; | |
384 | + } | |
385 | + | |
386 | + for (i = 0; i < optlen; ) { | |
387 | + switch (opt[i]) { | |
388 | + case 0: | |
389 | + end_of_options = 1; | |
390 | + i++; | |
391 | + break; | |
392 | + case 1: | |
393 | + i++; | |
394 | + break; | |
395 | + default: | |
396 | + /* CHECK: options after EOO. */ | |
397 | + if (end_of_options) { | |
398 | + limpk("TCP option %u after end\n", | |
399 | + opt[i]); | |
400 | + return 0; | |
401 | + } | |
402 | + /* CHECK: options at tail. */ | |
403 | + else if (i+1 >= optlen) { | |
404 | + limpk("TCP option %u at tail\n", | |
405 | + opt[i]); | |
406 | + return 0; | |
407 | + } | |
408 | + /* CHECK: zero-length options. */ | |
409 | + else if (opt[i+1] == 0) { | |
410 | + limpk("TCP option %u 0 len\n", | |
411 | + opt[i]); | |
412 | + return 0; | |
413 | + } | |
414 | + /* CHECK: oversize options. */ | |
415 | + else if (i + opt[i+1] > optlen) { | |
416 | + limpk("TCP option %u at %u too long\n", | |
417 | + (unsigned int) opt[i], i); | |
418 | + return 0; | |
419 | + } | |
420 | + /* Move to next option */ | |
421 | + i += opt[i+1]; | |
422 | + } | |
423 | + } | |
424 | + | |
425 | + return 1; | |
426 | +} | |
427 | + | |
428 | +/* Returns 1 if ok */ | |
429 | +/* Standard IP checks. */ | |
430 | +static int | |
431 | +check_ip(const struct sk_buff *skb, unsigned int offset) | |
432 | +{ | |
433 | + int end_of_options = 0; | |
434 | + unsigned int datalen, optlen; | |
435 | + unsigned int i; | |
436 | + unsigned int fragoff; | |
437 | + struct iphdr iph; | |
438 | + unsigned char opt[15 * 4 - sizeof(struct iphdr)]; | |
439 | + int embedded = offset; | |
440 | + | |
441 | + /* Should only happen for local outgoing raw-socket packets. */ | |
442 | + /* CHECK: length >= ip header. */ | |
443 | + if (skb_copy_bits(skb, offset, &iph, sizeof(iph)) < 0) { | |
444 | + limpk("Packet length %u < IP header.\n", skb->len - offset); | |
445 | + return 0; | |
446 | + } | |
447 | + if (iph.ihl * 4 < sizeof(iph)) { | |
448 | + limpk("IP len %u < minimum IP header.\n", iph.ihl*4); | |
449 | + return 0; | |
450 | + } | |
451 | + | |
452 | + optlen = iph.ihl * 4 - sizeof(iph); | |
453 | + if (skb_copy_bits(skb, offset+sizeof(struct iphdr), opt, optlen)<0) { | |
454 | + limpk("Packet length %u < IP header %u.\n", | |
455 | + skb->len - offset, iph.ihl * 4); | |
456 | + return 0; | |
457 | + } | |
458 | + | |
459 | + fragoff = (ntohs(iph.frag_off) & IP_OFFSET); | |
460 | + datalen = skb->len - (offset + sizeof(struct iphdr) + optlen); | |
461 | + | |
462 | + /* CHECK: Embedded fragment. */ | |
463 | + if (offset && fragoff) { | |
464 | + limpk("Embedded fragment.\n"); | |
465 | + return 0; | |
466 | + } | |
467 | + | |
468 | + for (i = 0; i < optlen; ) { | |
469 | + switch (opt[i]) { | |
470 | + case 0: | |
471 | + end_of_options = 1; | |
472 | + i++; | |
473 | + break; | |
474 | + case 1: | |
475 | + i++; | |
476 | + break; | |
477 | + default: | |
478 | + /* CHECK: options after EOO. */ | |
479 | + if (end_of_options) { | |
480 | + limpk("IP option %u after end\n", | |
481 | + opt[i]); | |
482 | + return 0; | |
483 | + } | |
484 | + /* CHECK: options at tail. */ | |
485 | + else if (i+1 >= optlen) { | |
486 | + limpk("IP option %u at tail\n", | |
487 | + opt[i]); | |
488 | + return 0; | |
489 | + } | |
490 | + /* CHECK: zero-length or one-length options. */ | |
491 | + else if (opt[i+1] < 2) { | |
492 | + limpk("IP option %u %u len\n", | |
493 | + opt[i], opt[i+1]); | |
494 | + return 0; | |
495 | + } | |
496 | + /* CHECK: oversize options. */ | |
497 | + else if (i + opt[i+1] > optlen) { | |
498 | + limpk("IP option %u at %u too long\n", | |
499 | + opt[i], i); | |
500 | + return 0; | |
501 | + } | |
502 | + /* Move to next option */ | |
503 | + i += opt[i+1]; | |
504 | + } | |
505 | + } | |
506 | + | |
507 | + /* Fragment checks. */ | |
508 | + | |
509 | + /* CHECK: More fragments, but doesn't fill 8-byte boundary. */ | |
510 | + if ((ntohs(iph.frag_off) & IP_MF) | |
511 | + && (ntohs(iph.tot_len) % 8) != 0) { | |
512 | + limpk("Truncated fragment %u long.\n", ntohs(iph.tot_len)); | |
513 | + return 0; | |
514 | + } | |
515 | + | |
516 | + /* CHECK: Oversize fragment a-la Ping of Death. */ | |
517 | + if (fragoff * 8 + datalen > 65535) { | |
518 | + limpk("Oversize fragment to %u.\n", fragoff * 8); | |
519 | + return 0; | |
520 | + } | |
521 | + | |
522 | + /* CHECK: DF set and fragoff or MF set. */ | |
523 | + if ((ntohs(iph.frag_off) & IP_DF) | |
524 | + && (fragoff || (ntohs(iph.frag_off) & IP_MF))) { | |
525 | + limpk("DF set and offset=%u, MF=%u.\n", | |
526 | + fragoff, ntohs(iph.frag_off) & IP_MF); | |
527 | + return 0; | |
528 | + } | |
529 | + | |
530 | + /* CHECK: Zero-sized fragments. */ | |
531 | + if ((fragoff || (ntohs(iph.frag_off) & IP_MF)) | |
532 | + && datalen == 0) { | |
533 | + limpk("Zero size fragment offset=%u\n", fragoff); | |
534 | + return 0; | |
535 | + } | |
536 | + | |
537 | + /* Note: we can have even middle fragments smaller than this: | |
538 | + consider a large packet passing through a 600MTU then | |
539 | + 576MTU link: this gives a fragment of 24 data bytes. But | |
540 | + everyone packs fragments largest first, hence a fragment | |
541 | + can't START before 576 - MAX_IP_HEADER_LEN. */ | |
542 | + | |
543 | + /* Used to be min-size 576: I recall Alan Cox saying ax25 goes | |
544 | + down to 128 (576 taken from RFC 791: All hosts must be | |
545 | + prepared to accept datagrams of up to 576 octets). Use 128 | |
546 | + here. */ | |
547 | +#define MIN_LIKELY_MTU 128 | |
548 | + /* CHECK: Min size of first frag = 128. */ | |
549 | + if ((ntohs(iph.frag_off) & IP_MF) | |
550 | + && fragoff == 0 | |
551 | + && ntohs(iph.tot_len) < MIN_LIKELY_MTU) { | |
552 | + limpk("First fragment size %u < %u\n", ntohs(iph.tot_len), | |
553 | + MIN_LIKELY_MTU); | |
554 | + return 0; | |
555 | + } | |
556 | + | |
557 | + /* CHECK: Min offset of frag = 128 - IP hdr len. */ | |
558 | + if (fragoff && fragoff * 8 < MIN_LIKELY_MTU - iph.ihl * 4) { | |
559 | + limpk("Fragment starts at %u < %u\n", fragoff * 8, | |
560 | + MIN_LIKELY_MTU - iph.ihl * 4); | |
561 | + return 0; | |
562 | + } | |
563 | + | |
564 | + /* CHECK: Protocol specification non-zero. */ | |
565 | + if (iph.protocol == 0) { | |
566 | + limpk("Zero protocol\n"); | |
567 | + return 0; | |
568 | + } | |
569 | + | |
570 | + /* FIXME: This is already checked for in "Oversize fragment" | |
571 | + above --RR */ | |
572 | + /* CHECK: Do not use what is unused. | |
573 | + * First bit of fragmentation flags should be unused. | |
574 | + * May be used by OS fingerprinting tools. | |
575 | + * 04 Jun 2002, Maciej Soltysiak, solt@dns.toxicfilms.tv | |
576 | + */ | |
577 | + if (ntohs(iph.frag_off)>>15) { | |
578 | + limpk("IP unused bit set\n"); | |
579 | + return 0; | |
580 | + } | |
581 | + | |
582 | + /* Per-protocol checks. */ | |
583 | + switch (iph.protocol) { | |
584 | + case IPPROTO_ICMP: | |
585 | + return check_icmp(skb, offset + iph.ihl*4, fragoff, | |
586 | + (ntohs(iph.frag_off) & IP_MF), | |
587 | + embedded); | |
588 | + | |
589 | + case IPPROTO_UDP: | |
590 | + return check_udp(skb, offset + iph.ihl*4, fragoff, | |
591 | + (ntohs(iph.frag_off) & IP_MF), | |
592 | + embedded); | |
593 | + | |
594 | + case IPPROTO_TCP: | |
595 | + return check_tcp(skb, offset + iph.ihl*4, fragoff, | |
596 | + (ntohs(iph.frag_off) & IP_MF), | |
597 | + embedded); | |
598 | + default: | |
599 | + /* Ignorance is bliss. */ | |
600 | + return 1; | |
601 | + } | |
602 | +} | |
603 | + | |
604 | +static int | |
605 | +match(const struct sk_buff *skb, | |
606 | + const struct net_device *in, | |
607 | + const struct net_device *out, | |
608 | + const void *matchinfo, | |
609 | + int offset, | |
610 | + unsigned int protoff, | |
611 | + int *hotdrop) | |
612 | +{ | |
613 | + return !check_ip(skb, 0); | |
614 | +} | |
615 | + | |
616 | +/* Called when user tries to insert an entry of this type. */ | |
617 | +static int | |
618 | +checkentry(const char *tablename, | |
619 | + const void *ip, | |
620 | + void *matchinfo, | |
621 | + unsigned int matchsize, | |
622 | + unsigned int hook_mask) | |
623 | +{ | |
624 | + if (matchsize != IPT_ALIGN(0)) | |
625 | + return 0; | |
626 | + | |
627 | + return 1; | |
628 | +} | |
629 | + | |
630 | +static struct ipt_match unclean_match = { | |
631 | + .name = "unclean", | |
632 | + .match = &match, | |
633 | + .checkentry = &checkentry, | |
634 | + .me = THIS_MODULE, | |
635 | +}; | |
636 | + | |
637 | +static int __init init(void) | |
638 | +{ | |
639 | + return ipt_register_match(&unclean_match); | |
640 | +} | |
641 | + | |
642 | +static void __exit fini(void) | |
643 | +{ | |
644 | + ipt_unregister_match(&unclean_match); | |
645 | +} | |
646 | + | |
647 | +module_init(init); | |
648 | +module_exit(fini); | |
649 | +MODULE_LICENSE("GPL"); |