aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/netfilter/Kbuild1
-rw-r--r--include/linux/netfilter/xt_TCPMSS.h10
-rw-r--r--include/linux/netfilter_ipv4/ipt_TCPMSS.h7
-rw-r--r--net/ipv4/netfilter/Kconfig26
-rw-r--r--net/ipv4/netfilter/Makefile1
-rw-r--r--net/ipv4/netfilter/ipt_TCPMSS.c207
-rw-r--r--net/netfilter/Kconfig26
-rw-r--r--net/netfilter/Makefile1
-rw-r--r--net/netfilter/xt_TCPMSS.c296
9 files changed, 337 insertions, 238 deletions
diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild
index 6328175a1c3a..43397a414cd6 100644
--- a/include/linux/netfilter/Kbuild
+++ b/include/linux/netfilter/Kbuild
@@ -33,6 +33,7 @@ header-y += xt_tcpmss.h
33header-y += xt_tcpudp.h 33header-y += xt_tcpudp.h
34header-y += xt_SECMARK.h 34header-y += xt_SECMARK.h
35header-y += xt_CONNSECMARK.h 35header-y += xt_CONNSECMARK.h
36header-y += xt_TCPMSS.h
36 37
37unifdef-y += nf_conntrack_common.h 38unifdef-y += nf_conntrack_common.h
38unifdef-y += nf_conntrack_ftp.h 39unifdef-y += nf_conntrack_ftp.h
diff --git a/include/linux/netfilter/xt_TCPMSS.h b/include/linux/netfilter/xt_TCPMSS.h
new file mode 100644
index 000000000000..53a292cd47f3
--- /dev/null
+++ b/include/linux/netfilter/xt_TCPMSS.h
@@ -0,0 +1,10 @@
1#ifndef _XT_TCPMSS_H
2#define _XT_TCPMSS_H
3
4struct xt_tcpmss_info {
5 u_int16_t mss;
6};
7
8#define XT_TCPMSS_CLAMP_PMTU 0xffff
9
10#endif /* _XT_TCPMSS_H */
diff --git a/include/linux/netfilter_ipv4/ipt_TCPMSS.h b/include/linux/netfilter_ipv4/ipt_TCPMSS.h
index aadb39580cd3..7a850f945824 100644
--- a/include/linux/netfilter_ipv4/ipt_TCPMSS.h
+++ b/include/linux/netfilter_ipv4/ipt_TCPMSS.h
@@ -1,10 +1,9 @@
1#ifndef _IPT_TCPMSS_H 1#ifndef _IPT_TCPMSS_H
2#define _IPT_TCPMSS_H 2#define _IPT_TCPMSS_H
3 3
4struct ipt_tcpmss_info { 4#include <linux/netfilter/xt_TCPMSS.h>
5 u_int16_t mss;
6};
7 5
8#define IPT_TCPMSS_CLAMP_PMTU 0xffff 6#define ipt_tcpmss_info xt_tcpmss_info
7#define IPT_TCPMSS_CLAMP_PMTU XT_TCPMSS_CLAMP_PMTU
9 8
10#endif /*_IPT_TCPMSS_H*/ 9#endif /*_IPT_TCPMSS_H*/
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
364config 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
391config IP_NF_NAT 365config 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
103obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o 103obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o
104obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o 104obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o
105obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o 105obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o
106obj-$(CONFIG_IP_NF_TARGET_TCPMSS) += ipt_TCPMSS.o
107obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o 106obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
108obj-$(CONFIG_IP_NF_TARGET_TTL) += ipt_TTL.o 107obj-$(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
20MODULE_LICENSE("GPL");
21MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
22MODULE_DESCRIPTION("iptables TCP MSS modification module");
23
24static inline unsigned int
25optlen(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
34static unsigned int
35ipt_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
149static 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 */
162static int
163ipt_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
187static 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
196static int __init ipt_tcpmss_init(void)
197{
198 return ipt_register_target(&ipt_tcpmss_reg);
199}
200
201static void __exit ipt_tcpmss_fini(void)
202{
203 ipt_unregister_target(&ipt_tcpmss_reg);
204}
205
206module_init(ipt_tcpmss_init);
207module_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
398config 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
398config NETFILTER_XT_MATCH_COMMENT 424config 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
45obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o 45obj-$(CONFIG_NETFILTER_XT_TARGET_NFLOG) += xt_NFLOG.o
46obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o 46obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o
47obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o 47obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o
48obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o
48obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o 49obj-$(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
25MODULE_LICENSE("GPL");
26MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
27MODULE_DESCRIPTION("x_tables TCP MSS modification module");
28MODULE_ALIAS("ipt_TCPMSS");
29MODULE_ALIAS("ip6t_TCPMSS");
30
31static inline unsigned int
32optlen(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
41static int
42tcpmss_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
140static unsigned int
141xt_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)
166static unsigned int
167xt_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 */
200static 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
212static int
213xt_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)
237static int
238xt_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
262static 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
285static int __init xt_tcpmss_init(void)
286{
287 return xt_register_targets(xt_tcpmss_reg, ARRAY_SIZE(xt_tcpmss_reg));
288}
289
290static void __exit xt_tcpmss_fini(void)
291{
292 xt_unregister_targets(xt_tcpmss_reg, ARRAY_SIZE(xt_tcpmss_reg));
293}
294
295module_init(xt_tcpmss_init);
296module_exit(xt_tcpmss_fini);