diff options
author | Patrick McHardy <kaber@trash.net> | 2007-02-07 18:09:46 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-02-08 15:39:16 -0500 |
commit | cdd289a2f833b93e65b9a09a02c37f47a58140a8 (patch) | |
tree | ed5172808cdd5b24605205732aca724a0fb9910d /net/ipv4 | |
parent | a8d0f9526ff8510d6fa5e708ef5386af19503299 (diff) |
[NETFILTER]: add IPv6-capable TCPMSS target
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/netfilter/Kconfig | 26 | ||||
-rw-r--r-- | net/ipv4/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/ipv4/netfilter/ipt_TCPMSS.c | 207 |
3 files changed, 0 insertions, 234 deletions
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 47bd3ad18b71..9b08e7ad71bc 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig | |||
@@ -361,32 +361,6 @@ config IP_NF_TARGET_ULOG | |||
361 | 361 | ||
362 | To compile it as a module, choose M here. If unsure, say N. | 362 | To compile it as a module, choose M here. If unsure, say N. |
363 | 363 | ||
364 | config IP_NF_TARGET_TCPMSS | ||
365 | tristate "TCPMSS target support" | ||
366 | depends on IP_NF_IPTABLES | ||
367 | ---help--- | ||
368 | This option adds a `TCPMSS' target, which allows you to alter the | ||
369 | MSS value of TCP SYN packets, to control the maximum size for that | ||
370 | connection (usually limiting it to your outgoing interface's MTU | ||
371 | minus 40). | ||
372 | |||
373 | This is used to overcome criminally braindead ISPs or servers which | ||
374 | block ICMP Fragmentation Needed packets. The symptoms of this | ||
375 | problem are that everything works fine from your Linux | ||
376 | firewall/router, but machines behind it can never exchange large | ||
377 | packets: | ||
378 | 1) Web browsers connect, then hang with no data received. | ||
379 | 2) Small mail works fine, but large emails hang. | ||
380 | 3) ssh works fine, but scp hangs after initial handshaking. | ||
381 | |||
382 | Workaround: activate this option and add a rule to your firewall | ||
383 | configuration like: | ||
384 | |||
385 | iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN \ | ||
386 | -j TCPMSS --clamp-mss-to-pmtu | ||
387 | |||
388 | To compile it as a module, choose M here. If unsure, say N. | ||
389 | |||
390 | # NAT + specific targets: ip_conntrack | 364 | # NAT + specific targets: ip_conntrack |
391 | config IP_NF_NAT | 365 | config IP_NF_NAT |
392 | tristate "Full NAT" | 366 | tristate "Full NAT" |
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 16d177b71bf8..6625ec68180c 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile | |||
@@ -103,7 +103,6 @@ obj-$(CONFIG_IP_NF_TARGET_SAME) += ipt_SAME.o | |||
103 | obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o | 103 | obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o |
104 | obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o | 104 | obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o |
105 | obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o | 105 | obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o |
106 | obj-$(CONFIG_IP_NF_TARGET_TCPMSS) += ipt_TCPMSS.o | ||
107 | obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o | 106 | obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o |
108 | obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o | 107 | obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o |
109 | 108 | ||
diff --git a/net/ipv4/netfilter/ipt_TCPMSS.c b/net/ipv4/netfilter/ipt_TCPMSS.c deleted file mode 100644 index 93eb5c3c1884..000000000000 --- a/net/ipv4/netfilter/ipt_TCPMSS.c +++ /dev/null | |||
@@ -1,207 +0,0 @@ | |||
1 | /* | ||
2 | * This is a module which is used for setting the MSS option in TCP packets. | ||
3 | * | ||
4 | * Copyright (C) 2000 Marc Boucher <marc@mbsi.ca> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/module.h> | ||
12 | #include <linux/skbuff.h> | ||
13 | |||
14 | #include <linux/ip.h> | ||
15 | #include <net/tcp.h> | ||
16 | |||
17 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
18 | #include <linux/netfilter_ipv4/ipt_TCPMSS.h> | ||
19 | |||
20 | MODULE_LICENSE("GPL"); | ||
21 | MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>"); | ||
22 | MODULE_DESCRIPTION("iptables TCP MSS modification module"); | ||
23 | |||
24 | static inline unsigned int | ||
25 | optlen(const u_int8_t *opt, unsigned int offset) | ||
26 | { | ||
27 | /* Beware zero-length options: make finite progress */ | ||
28 | if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0) | ||
29 | return 1; | ||
30 | else | ||
31 | return opt[offset+1]; | ||
32 | } | ||
33 | |||
34 | static unsigned int | ||
35 | ipt_tcpmss_target(struct sk_buff **pskb, | ||
36 | const struct net_device *in, | ||
37 | const struct net_device *out, | ||
38 | unsigned int hooknum, | ||
39 | const struct xt_target *target, | ||
40 | const void *targinfo) | ||
41 | { | ||
42 | const struct ipt_tcpmss_info *tcpmssinfo = targinfo; | ||
43 | struct tcphdr *tcph; | ||
44 | struct iphdr *iph; | ||
45 | u_int16_t tcplen, newmss; | ||
46 | __be16 newtotlen, oldval; | ||
47 | unsigned int i; | ||
48 | u_int8_t *opt; | ||
49 | |||
50 | if (!skb_make_writable(pskb, (*pskb)->len)) | ||
51 | return NF_DROP; | ||
52 | |||
53 | iph = (*pskb)->nh.iph; | ||
54 | tcplen = (*pskb)->len - iph->ihl*4; | ||
55 | tcph = (void *)iph + iph->ihl*4; | ||
56 | |||
57 | /* Since it passed flags test in tcp match, we know it is is | ||
58 | not a fragment, and has data >= tcp header length. SYN | ||
59 | packets should not contain data: if they did, then we risk | ||
60 | running over MTU, sending Frag Needed and breaking things | ||
61 | badly. --RR */ | ||
62 | if (tcplen != tcph->doff*4) { | ||
63 | if (net_ratelimit()) | ||
64 | printk(KERN_ERR | ||
65 | "ipt_tcpmss_target: bad length (%d bytes)\n", | ||
66 | (*pskb)->len); | ||
67 | return NF_DROP; | ||
68 | } | ||
69 | |||
70 | if (tcpmssinfo->mss == IPT_TCPMSS_CLAMP_PMTU) { | ||
71 | if (dst_mtu((*pskb)->dst) <= sizeof(struct iphdr) + | ||
72 | sizeof(struct tcphdr)) { | ||
73 | if (net_ratelimit()) | ||
74 | printk(KERN_ERR "ipt_tcpmss_target: " | ||
75 | "unknown or invalid path-MTU (%d)\n", | ||
76 | dst_mtu((*pskb)->dst)); | ||
77 | return NF_DROP; /* or IPT_CONTINUE ?? */ | ||
78 | } | ||
79 | |||
80 | newmss = dst_mtu((*pskb)->dst) - sizeof(struct iphdr) - | ||
81 | sizeof(struct tcphdr); | ||
82 | } else | ||
83 | newmss = tcpmssinfo->mss; | ||
84 | |||
85 | opt = (u_int8_t *)tcph; | ||
86 | for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) { | ||
87 | if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS && | ||
88 | opt[i+1] == TCPOLEN_MSS) { | ||
89 | u_int16_t oldmss; | ||
90 | |||
91 | oldmss = (opt[i+2] << 8) | opt[i+3]; | ||
92 | |||
93 | if (tcpmssinfo->mss == IPT_TCPMSS_CLAMP_PMTU && | ||
94 | oldmss <= newmss) | ||
95 | return IPT_CONTINUE; | ||
96 | |||
97 | opt[i+2] = (newmss & 0xff00) >> 8; | ||
98 | opt[i+3] = (newmss & 0x00ff); | ||
99 | |||
100 | nf_proto_csum_replace2(&tcph->check, *pskb, | ||
101 | htons(oldmss), htons(newmss), 0); | ||
102 | return IPT_CONTINUE; | ||
103 | } | ||
104 | } | ||
105 | |||
106 | /* | ||
107 | * MSS Option not found ?! add it.. | ||
108 | */ | ||
109 | if (skb_tailroom((*pskb)) < TCPOLEN_MSS) { | ||
110 | struct sk_buff *newskb; | ||
111 | |||
112 | newskb = skb_copy_expand(*pskb, skb_headroom(*pskb), | ||
113 | TCPOLEN_MSS, GFP_ATOMIC); | ||
114 | if (!newskb) | ||
115 | return NF_DROP; | ||
116 | kfree_skb(*pskb); | ||
117 | *pskb = newskb; | ||
118 | iph = (*pskb)->nh.iph; | ||
119 | tcph = (void *)iph + iph->ihl*4; | ||
120 | } | ||
121 | |||
122 | skb_put((*pskb), TCPOLEN_MSS); | ||
123 | |||
124 | opt = (u_int8_t *)tcph + sizeof(struct tcphdr); | ||
125 | memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr)); | ||
126 | |||
127 | nf_proto_csum_replace2(&tcph->check, *pskb, | ||
128 | htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1); | ||
129 | opt[0] = TCPOPT_MSS; | ||
130 | opt[1] = TCPOLEN_MSS; | ||
131 | opt[2] = (newmss & 0xff00) >> 8; | ||
132 | opt[3] = (newmss & 0x00ff); | ||
133 | |||
134 | nf_proto_csum_replace4(&tcph->check, *pskb, 0, *((__be32 *)opt), 0); | ||
135 | |||
136 | oldval = ((__be16 *)tcph)[6]; | ||
137 | tcph->doff += TCPOLEN_MSS/4; | ||
138 | nf_proto_csum_replace2(&tcph->check, *pskb, | ||
139 | oldval, ((__be16 *)tcph)[6], 0); | ||
140 | |||
141 | newtotlen = htons(ntohs(iph->tot_len) + TCPOLEN_MSS); | ||
142 | nf_csum_replace2(&iph->check, iph->tot_len, newtotlen); | ||
143 | iph->tot_len = newtotlen; | ||
144 | return IPT_CONTINUE; | ||
145 | } | ||
146 | |||
147 | #define TH_SYN 0x02 | ||
148 | |||
149 | static inline int find_syn_match(const struct ipt_entry_match *m) | ||
150 | { | ||
151 | const struct ipt_tcp *tcpinfo = (const struct ipt_tcp *)m->data; | ||
152 | |||
153 | if (strcmp(m->u.kernel.match->name, "tcp") == 0 && | ||
154 | tcpinfo->flg_cmp & TH_SYN && | ||
155 | !(tcpinfo->invflags & IPT_TCP_INV_FLAGS)) | ||
156 | return 1; | ||
157 | |||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | /* Must specify -p tcp --syn/--tcp-flags SYN */ | ||
162 | static int | ||
163 | ipt_tcpmss_checkentry(const char *tablename, | ||
164 | const void *e_void, | ||
165 | const struct xt_target *target, | ||
166 | void *targinfo, | ||
167 | unsigned int hook_mask) | ||
168 | { | ||
169 | const struct ipt_tcpmss_info *tcpmssinfo = targinfo; | ||
170 | const struct ipt_entry *e = e_void; | ||
171 | |||
172 | if (tcpmssinfo->mss == IPT_TCPMSS_CLAMP_PMTU && | ||
173 | (hook_mask & ~((1 << NF_IP_FORWARD) | | ||
174 | (1 << NF_IP_LOCAL_OUT) | | ||
175 | (1 << NF_IP_POST_ROUTING))) != 0) { | ||
176 | printk("TCPMSS: path-MTU clamping only supported in " | ||
177 | "FORWARD, OUTPUT and POSTROUTING hooks\n"); | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | if (IPT_MATCH_ITERATE(e, find_syn_match)) | ||
182 | return 1; | ||
183 | printk("TCPMSS: Only works on TCP SYN packets\n"); | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static struct ipt_target ipt_tcpmss_reg = { | ||
188 | .name = "TCPMSS", | ||
189 | .target = ipt_tcpmss_target, | ||
190 | .targetsize = sizeof(struct ipt_tcpmss_info), | ||
191 | .proto = IPPROTO_TCP, | ||
192 | .checkentry = ipt_tcpmss_checkentry, | ||
193 | .me = THIS_MODULE, | ||
194 | }; | ||
195 | |||
196 | static int __init ipt_tcpmss_init(void) | ||
197 | { | ||
198 | return ipt_register_target(&ipt_tcpmss_reg); | ||
199 | } | ||
200 | |||
201 | static void __exit ipt_tcpmss_fini(void) | ||
202 | { | ||
203 | ipt_unregister_target(&ipt_tcpmss_reg); | ||
204 | } | ||
205 | |||
206 | module_init(ipt_tcpmss_init); | ||
207 | module_exit(ipt_tcpmss_fini); | ||