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 | |
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')
-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 | ||||
-rw-r--r-- | net/netfilter/Kconfig | 26 | ||||
-rw-r--r-- | net/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/xt_TCPMSS.c | 296 |
6 files changed, 323 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); | ||
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 614c92c17835..748f7f00909a 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
@@ -395,6 +395,32 @@ config NETFILTER_XT_TARGET_CONNSECMARK | |||
395 | 395 | ||
396 | To compile it as a module, choose M here. If unsure, say N. | 396 | To compile it as a module, choose M here. If unsure, say N. |
397 | 397 | ||
398 | config NETFILTER_XT_TARGET_TCPMSS | ||
399 | tristate '"TCPMSS" target support' | ||
400 | depends on NETFILTER_XTABLES && (IPV6 || IPV6=n) | ||
401 | ---help--- | ||
402 | This option adds a `TCPMSS' target, which allows you to alter the | ||
403 | MSS value of TCP SYN packets, to control the maximum size for that | ||
404 | connection (usually limiting it to your outgoing interface's MTU | ||
405 | minus 40). | ||
406 | |||
407 | This is used to overcome criminally braindead ISPs or servers which | ||
408 | block ICMP Fragmentation Needed packets. The symptoms of this | ||
409 | problem are that everything works fine from your Linux | ||
410 | firewall/router, but machines behind it can never exchange large | ||
411 | packets: | ||
412 | 1) Web browsers connect, then hang with no data received. | ||
413 | 2) Small mail works fine, but large emails hang. | ||
414 | 3) ssh works fine, but scp hangs after initial handshaking. | ||
415 | |||
416 | Workaround: activate this option and add a rule to your firewall | ||
417 | configuration like: | ||
418 | |||
419 | iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN \ | ||
420 | -j TCPMSS --clamp-mss-to-pmtu | ||
421 | |||
422 | To compile it as a module, choose M here. If unsure, say N. | ||
423 | |||
398 | config NETFILTER_XT_MATCH_COMMENT | 424 | config NETFILTER_XT_MATCH_COMMENT |
399 | tristate '"comment" match support' | 425 | tristate '"comment" match support' |
400 | depends on NETFILTER_XTABLES | 426 | depends on NETFILTER_XTABLES |
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 5054b0ff8096..b2b5c7566b26 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -45,6 +45,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o | |||
45 | obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o | 45 | obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o |
46 | obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o | 46 | obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o |
47 | obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o | 47 | obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o |
48 | obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o | ||
48 | obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o | 49 | obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o |
49 | 50 | ||
50 | # matches | 51 | # matches |
diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c new file mode 100644 index 000000000000..db7e38c08de2 --- /dev/null +++ b/net/netfilter/xt_TCPMSS.c | |||
@@ -0,0 +1,296 @@ | |||
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 | #include <linux/ip.h> | ||
14 | #include <linux/ipv6.h> | ||
15 | #include <linux/tcp.h> | ||
16 | #include <net/ipv6.h> | ||
17 | #include <net/tcp.h> | ||
18 | |||
19 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
20 | #include <linux/netfilter_ipv6/ip6_tables.h> | ||
21 | #include <linux/netfilter/x_tables.h> | ||
22 | #include <linux/netfilter/xt_tcpudp.h> | ||
23 | #include <linux/netfilter/xt_TCPMSS.h> | ||
24 | |||
25 | MODULE_LICENSE("GPL"); | ||
26 | MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>"); | ||
27 | MODULE_DESCRIPTION("x_tables TCP MSS modification module"); | ||
28 | MODULE_ALIAS("ipt_TCPMSS"); | ||
29 | MODULE_ALIAS("ip6t_TCPMSS"); | ||
30 | |||
31 | static inline unsigned int | ||
32 | optlen(const u_int8_t *opt, unsigned int offset) | ||
33 | { | ||
34 | /* Beware zero-length options: make finite progress */ | ||
35 | if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0) | ||
36 | return 1; | ||
37 | else | ||
38 | return opt[offset+1]; | ||
39 | } | ||
40 | |||
41 | static int | ||
42 | tcpmss_mangle_packet(struct sk_buff **pskb, | ||
43 | const struct xt_tcpmss_info *info, | ||
44 | unsigned int tcphoff, | ||
45 | unsigned int minlen) | ||
46 | { | ||
47 | struct tcphdr *tcph; | ||
48 | unsigned int tcplen, i; | ||
49 | __be16 oldval; | ||
50 | u16 newmss; | ||
51 | u8 *opt; | ||
52 | |||
53 | if (!skb_make_writable(pskb, (*pskb)->len)) | ||
54 | return -1; | ||
55 | |||
56 | tcplen = (*pskb)->len - tcphoff; | ||
57 | tcph = (struct tcphdr *)((*pskb)->nh.raw + tcphoff); | ||
58 | |||
59 | /* Since it passed flags test in tcp match, we know it is is | ||
60 | not a fragment, and has data >= tcp header length. SYN | ||
61 | packets should not contain data: if they did, then we risk | ||
62 | running over MTU, sending Frag Needed and breaking things | ||
63 | badly. --RR */ | ||
64 | if (tcplen != tcph->doff*4) { | ||
65 | if (net_ratelimit()) | ||
66 | printk(KERN_ERR "xt_TCPMSS: bad length (%u bytes)\n", | ||
67 | (*pskb)->len); | ||
68 | return -1; | ||
69 | } | ||
70 | |||
71 | if (info->mss == XT_TCPMSS_CLAMP_PMTU) { | ||
72 | if (dst_mtu((*pskb)->dst) <= minlen) { | ||
73 | if (net_ratelimit()) | ||
74 | printk(KERN_ERR "xt_TCPMSS: " | ||
75 | "unknown or invalid path-MTU (%u)\n", | ||
76 | dst_mtu((*pskb)->dst)); | ||
77 | return -1; | ||
78 | } | ||
79 | newmss = dst_mtu((*pskb)->dst) - minlen; | ||
80 | } else | ||
81 | newmss = info->mss; | ||
82 | |||
83 | opt = (u_int8_t *)tcph; | ||
84 | for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) { | ||
85 | if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS && | ||
86 | opt[i+1] == TCPOLEN_MSS) { | ||
87 | u_int16_t oldmss; | ||
88 | |||
89 | oldmss = (opt[i+2] << 8) | opt[i+3]; | ||
90 | |||
91 | if (info->mss == XT_TCPMSS_CLAMP_PMTU && | ||
92 | oldmss <= newmss) | ||
93 | return 0; | ||
94 | |||
95 | opt[i+2] = (newmss & 0xff00) >> 8; | ||
96 | opt[i+3] = (newmss & 0x00ff); | ||
97 | |||
98 | nf_proto_csum_replace2(&tcph->check, *pskb, | ||
99 | htons(oldmss), htons(newmss), 0); | ||
100 | return 0; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | /* | ||
105 | * MSS Option not found ?! add it.. | ||
106 | */ | ||
107 | if (skb_tailroom((*pskb)) < TCPOLEN_MSS) { | ||
108 | struct sk_buff *newskb; | ||
109 | |||
110 | newskb = skb_copy_expand(*pskb, skb_headroom(*pskb), | ||
111 | TCPOLEN_MSS, GFP_ATOMIC); | ||
112 | if (!newskb) | ||
113 | return -1; | ||
114 | kfree_skb(*pskb); | ||
115 | *pskb = newskb; | ||
116 | tcph = (struct tcphdr *)((*pskb)->nh.raw + tcphoff); | ||
117 | } | ||
118 | |||
119 | skb_put((*pskb), TCPOLEN_MSS); | ||
120 | |||
121 | opt = (u_int8_t *)tcph + sizeof(struct tcphdr); | ||
122 | memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr)); | ||
123 | |||
124 | nf_proto_csum_replace2(&tcph->check, *pskb, | ||
125 | htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1); | ||
126 | opt[0] = TCPOPT_MSS; | ||
127 | opt[1] = TCPOLEN_MSS; | ||
128 | opt[2] = (newmss & 0xff00) >> 8; | ||
129 | opt[3] = (newmss & 0x00ff); | ||
130 | |||
131 | nf_proto_csum_replace4(&tcph->check, *pskb, 0, *((__be32 *)opt), 0); | ||
132 | |||
133 | oldval = ((__be16 *)tcph)[6]; | ||
134 | tcph->doff += TCPOLEN_MSS/4; | ||
135 | nf_proto_csum_replace2(&tcph->check, *pskb, | ||
136 | oldval, ((__be16 *)tcph)[6], 0); | ||
137 | return TCPOLEN_MSS; | ||
138 | } | ||
139 | |||
140 | static unsigned int | ||
141 | xt_tcpmss_target4(struct sk_buff **pskb, | ||
142 | const struct net_device *in, | ||
143 | const struct net_device *out, | ||
144 | unsigned int hooknum, | ||
145 | const struct xt_target *target, | ||
146 | const void *targinfo) | ||
147 | { | ||
148 | struct iphdr *iph = (*pskb)->nh.iph; | ||
149 | __be16 newlen; | ||
150 | int ret; | ||
151 | |||
152 | ret = tcpmss_mangle_packet(pskb, targinfo, iph->ihl * 4, | ||
153 | sizeof(*iph) + sizeof(struct tcphdr)); | ||
154 | if (ret < 0) | ||
155 | return NF_DROP; | ||
156 | if (ret > 0) { | ||
157 | iph = (*pskb)->nh.iph; | ||
158 | newlen = htons(ntohs(iph->tot_len) + ret); | ||
159 | nf_csum_replace2(&iph->check, iph->tot_len, newlen); | ||
160 | iph->tot_len = newlen; | ||
161 | } | ||
162 | return XT_CONTINUE; | ||
163 | } | ||
164 | |||
165 | #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) | ||
166 | static unsigned int | ||
167 | xt_tcpmss_target6(struct sk_buff **pskb, | ||
168 | const struct net_device *in, | ||
169 | const struct net_device *out, | ||
170 | unsigned int hooknum, | ||
171 | const struct xt_target *target, | ||
172 | const void *targinfo) | ||
173 | { | ||
174 | struct ipv6hdr *ipv6h = (*pskb)->nh.ipv6h; | ||
175 | u8 nexthdr; | ||
176 | int tcphoff; | ||
177 | int ret; | ||
178 | |||
179 | nexthdr = ipv6h->nexthdr; | ||
180 | tcphoff = ipv6_skip_exthdr(*pskb, sizeof(*ipv6h), &nexthdr); | ||
181 | if (tcphoff < 0) { | ||
182 | WARN_ON(1); | ||
183 | return NF_DROP; | ||
184 | } | ||
185 | ret = tcpmss_mangle_packet(pskb, targinfo, tcphoff, | ||
186 | sizeof(*ipv6h) + sizeof(struct tcphdr)); | ||
187 | if (ret < 0) | ||
188 | return NF_DROP; | ||
189 | if (ret > 0) { | ||
190 | ipv6h = (*pskb)->nh.ipv6h; | ||
191 | ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) + ret); | ||
192 | } | ||
193 | return XT_CONTINUE; | ||
194 | } | ||
195 | #endif | ||
196 | |||
197 | #define TH_SYN 0x02 | ||
198 | |||
199 | /* Must specify -p tcp --syn */ | ||
200 | static inline int find_syn_match(const struct xt_entry_match *m) | ||
201 | { | ||
202 | const struct xt_tcp *tcpinfo = (const struct xt_tcp *)m->data; | ||
203 | |||
204 | if (strcmp(m->u.kernel.match->name, "tcp") == 0 && | ||
205 | tcpinfo->flg_cmp & TH_SYN && | ||
206 | !(tcpinfo->invflags & XT_TCP_INV_FLAGS)) | ||
207 | return 1; | ||
208 | |||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | static int | ||
213 | xt_tcpmss_checkentry4(const char *tablename, | ||
214 | const void *entry, | ||
215 | const struct xt_target *target, | ||
216 | void *targinfo, | ||
217 | unsigned int hook_mask) | ||
218 | { | ||
219 | const struct xt_tcpmss_info *info = targinfo; | ||
220 | const struct ipt_entry *e = entry; | ||
221 | |||
222 | if (info->mss == XT_TCPMSS_CLAMP_PMTU && | ||
223 | (hook_mask & ~((1 << NF_IP_FORWARD) | | ||
224 | (1 << NF_IP_LOCAL_OUT) | | ||
225 | (1 << NF_IP_POST_ROUTING))) != 0) { | ||
226 | printk("xt_TCPMSS: path-MTU clamping only supported in " | ||
227 | "FORWARD, OUTPUT and POSTROUTING hooks\n"); | ||
228 | return 0; | ||
229 | } | ||
230 | if (IPT_MATCH_ITERATE(e, find_syn_match)) | ||
231 | return 1; | ||
232 | printk("xt_TCPMSS: Only works on TCP SYN packets\n"); | ||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) | ||
237 | static int | ||
238 | xt_tcpmss_checkentry6(const char *tablename, | ||
239 | const void *entry, | ||
240 | const struct xt_target *target, | ||
241 | void *targinfo, | ||
242 | unsigned int hook_mask) | ||
243 | { | ||
244 | const struct xt_tcpmss_info *info = targinfo; | ||
245 | const struct ip6t_entry *e = entry; | ||
246 | |||
247 | if (info->mss == XT_TCPMSS_CLAMP_PMTU && | ||
248 | (hook_mask & ~((1 << NF_IP6_FORWARD) | | ||
249 | (1 << NF_IP6_LOCAL_OUT) | | ||
250 | (1 << NF_IP6_POST_ROUTING))) != 0) { | ||
251 | printk("xt_TCPMSS: path-MTU clamping only supported in " | ||
252 | "FORWARD, OUTPUT and POSTROUTING hooks\n"); | ||
253 | return 0; | ||
254 | } | ||
255 | if (IP6T_MATCH_ITERATE(e, find_syn_match)) | ||
256 | return 1; | ||
257 | printk("xt_TCPMSS: Only works on TCP SYN packets\n"); | ||
258 | return 0; | ||
259 | } | ||
260 | #endif | ||
261 | |||
262 | static struct xt_target xt_tcpmss_reg[] = { | ||
263 | { | ||
264 | .family = AF_INET, | ||
265 | .name = "TCPMSS", | ||
266 | .checkentry = xt_tcpmss_checkentry4, | ||
267 | .target = xt_tcpmss_target4, | ||
268 | .targetsize = sizeof(struct xt_tcpmss_info), | ||
269 | .proto = IPPROTO_TCP, | ||
270 | .me = THIS_MODULE, | ||
271 | }, | ||
272 | #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) | ||
273 | { | ||
274 | .family = AF_INET6, | ||
275 | .name = "TCPMSS", | ||
276 | .checkentry = xt_tcpmss_checkentry6, | ||
277 | .target = xt_tcpmss_target6, | ||
278 | .targetsize = sizeof(struct xt_tcpmss_info), | ||
279 | .proto = IPPROTO_TCP, | ||
280 | .me = THIS_MODULE, | ||
281 | }, | ||
282 | #endif | ||
283 | }; | ||
284 | |||
285 | static int __init xt_tcpmss_init(void) | ||
286 | { | ||
287 | return xt_register_targets(xt_tcpmss_reg, ARRAY_SIZE(xt_tcpmss_reg)); | ||
288 | } | ||
289 | |||
290 | static void __exit xt_tcpmss_fini(void) | ||
291 | { | ||
292 | xt_unregister_targets(xt_tcpmss_reg, ARRAY_SIZE(xt_tcpmss_reg)); | ||
293 | } | ||
294 | |||
295 | module_init(xt_tcpmss_init); | ||
296 | module_exit(xt_tcpmss_fini); | ||