allow coexistance of N build and AC build.
[tomato.git] / release / src-rt-6.x / linux / linux-2.6 / net / netfilter / xt_TCPMSS.c
blob73537643c1e388bc6edec149f426dfb4828fdfd1
1 /*
2 * This is a module which is used for setting the MSS option in TCP packets.
4 * Copyright (C) 2000 Marc Boucher <marc@mbsi.ca>
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 */
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/dst.h>
17 #include <net/flow.h>
18 #include <net/ipv6.h>
19 #include <net/route.h>
20 #include <net/ip6_route.h>
21 #include <net/tcp.h>
23 #include <linux/netfilter_ipv4/ip_tables.h>
24 #include <linux/netfilter_ipv6/ip6_tables.h>
25 #include <linux/netfilter/x_tables.h>
26 #include <linux/netfilter/xt_tcpudp.h>
27 #include <linux/netfilter/xt_TCPMSS.h>
29 MODULE_LICENSE("GPL");
30 MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
31 MODULE_DESCRIPTION("x_tables TCP Maximum Segment Size (MSS) adjustment");
32 MODULE_ALIAS("ipt_TCPMSS");
33 MODULE_ALIAS("ip6t_TCPMSS");
35 static inline unsigned int
36 optlen(const u_int8_t *opt, unsigned int offset)
38 /* Beware zero-length options: make finite progress */
39 if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0)
40 return 1;
41 else
42 return opt[offset+1];
45 static int
46 tcpmss_mangle_packet(struct sk_buff *skb,
47 const struct xt_tcpmss_info *info,
48 unsigned int in_mtu,
49 unsigned int tcphoff,
50 unsigned int minlen)
52 struct tcphdr *tcph;
53 unsigned int tcplen, i;
54 __be16 oldval;
55 u16 newmss;
56 u8 *opt;
58 if (!skb_make_writable(skb, skb->len))
59 return -1;
61 tcplen = skb->len - tcphoff;
62 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
64 /* Header cannot be larger than the packet */
65 if (tcplen < tcph->doff*4)
66 return -1;
68 if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
69 if (dst_mtu(skb->dst) <= minlen) {
70 if (net_ratelimit())
71 printk(KERN_ERR "xt_TCPMSS: "
72 "unknown or invalid path-MTU (%u)\n",
73 dst_mtu(skb->dst));
74 return -1;
76 if (in_mtu <= minlen) {
77 if (net_ratelimit())
78 printk(KERN_ERR "xt_TCPMSS: unknown or "
79 "invalid path-MTU (%u)\n", in_mtu);
80 return -1;
82 newmss = min(dst_mtu(skb->dst), in_mtu) - minlen;
83 } else
84 newmss = info->mss;
86 opt = (u_int8_t *)tcph;
87 for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) {
88 if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS &&
89 opt[i+1] == TCPOLEN_MSS) {
90 u_int16_t oldmss;
92 oldmss = (opt[i+2] << 8) | opt[i+3];
94 /* Never increase MSS, even when setting it, as
95 * doing so results in problems for hosts that rely
96 * on MSS being set correctly.
98 if (oldmss <= newmss)
99 return 0;
101 opt[i+2] = (newmss & 0xff00) >> 8;
102 opt[i+3] = (newmss & 0x00ff);
104 nf_proto_csum_replace2(&tcph->check, skb,
105 htons(oldmss), htons(newmss), 0);
106 return 0;
110 /* There is data after the header so the option can't be added
111 without moving it, and doing so may make the SYN packet
112 itself too large. Accept the packet unmodified instead. */
113 if (tcplen > tcph->doff*4)
114 return 0;
117 * MSS Option not found ?! add it..
119 if (skb_tailroom(skb) < TCPOLEN_MSS) {
120 if (pskb_expand_head(skb, 0,
121 TCPOLEN_MSS - skb_tailroom(skb),
122 GFP_ATOMIC))
123 return -1;
124 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
127 skb_put(skb, TCPOLEN_MSS);
129 opt = (u_int8_t *)tcph + sizeof(struct tcphdr);
130 memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr));
132 nf_proto_csum_replace2(&tcph->check, skb,
133 htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1);
134 opt[0] = TCPOPT_MSS;
135 opt[1] = TCPOLEN_MSS;
136 opt[2] = (newmss & 0xff00) >> 8;
137 opt[3] = (newmss & 0x00ff);
139 nf_proto_csum_replace4(&tcph->check, skb, 0, *((__be32 *)opt), 0);
141 oldval = ((__be16 *)tcph)[6];
142 tcph->doff += TCPOLEN_MSS/4;
143 nf_proto_csum_replace2(&tcph->check, skb,
144 oldval, ((__be16 *)tcph)[6], 0);
145 return TCPOLEN_MSS;
148 static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb,
149 unsigned int family)
151 struct flowi fl = {};
152 struct rtable *rt = NULL;
153 u_int32_t mtu = ~0U;
155 if (family == PF_INET)
156 fl.fl4_dst = ip_hdr(skb)->saddr;
157 else
158 fl.fl6_dst = ipv6_hdr(skb)->saddr;
160 rcu_read_lock();
161 if (family == PF_INET)
162 ip_route_output_key(&rt, &fl);
163 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
164 else
165 rt = (struct rtable *)ip6_route_output(NULL, &fl);
166 #endif
167 rcu_read_unlock();
169 if (rt != NULL) {
170 mtu = dst_mtu(&rt->u.dst);
171 dst_release(&rt->u.dst);
173 return mtu;
176 static unsigned int
177 xt_tcpmss_target4(struct sk_buff *skb,
178 const struct net_device *in,
179 const struct net_device *out,
180 unsigned int hooknum,
181 const struct xt_target *target,
182 const void *targinfo)
184 struct iphdr *iph = ip_hdr(skb);
185 __be16 newlen;
186 int ret;
188 ret = tcpmss_mangle_packet(skb, targinfo,
189 tcpmss_reverse_mtu(skb, PF_INET),
190 iph->ihl * 4,
191 sizeof(*iph) + sizeof(struct tcphdr));
192 if (ret < 0)
193 return NF_DROP;
194 if (ret > 0) {
195 iph = ip_hdr(skb);
196 newlen = htons(ntohs(iph->tot_len) + ret);
197 nf_csum_replace2(&iph->check, iph->tot_len, newlen);
198 iph->tot_len = newlen;
200 return XT_CONTINUE;
203 #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
204 static unsigned int
205 xt_tcpmss_target6(struct sk_buff *skb,
206 const struct net_device *in,
207 const struct net_device *out,
208 unsigned int hooknum,
209 const struct xt_target *target,
210 const void *targinfo)
212 struct ipv6hdr *ipv6h = ipv6_hdr(skb);
213 u8 nexthdr;
214 int tcphoff;
215 int ret;
217 nexthdr = ipv6h->nexthdr;
218 tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr);
219 if (tcphoff < 0)
220 return NF_DROP;
221 ret = tcpmss_mangle_packet(skb, targinfo,
222 tcpmss_reverse_mtu(skb, PF_INET6),
223 tcphoff,
224 sizeof(*ipv6h) + sizeof(struct tcphdr));
225 if (ret < 0)
226 return NF_DROP;
227 if (ret > 0) {
228 ipv6h = ipv6_hdr(skb);
229 ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) + ret);
231 return XT_CONTINUE;
233 #endif
235 #define TH_SYN 0x02
237 /* Must specify -p tcp --syn */
238 static inline int find_syn_match(const struct xt_entry_match *m)
240 const struct xt_tcp *tcpinfo = (const struct xt_tcp *)m->data;
242 if (strcmp(m->u.kernel.match->name, "tcp") == 0 &&
243 tcpinfo->flg_cmp & TH_SYN &&
244 !(tcpinfo->invflags & XT_TCP_INV_FLAGS))
245 return 1;
247 return 0;
250 static int
251 xt_tcpmss_checkentry4(const char *tablename,
252 const void *entry,
253 const struct xt_target *target,
254 void *targinfo,
255 unsigned int hook_mask)
257 const struct xt_tcpmss_info *info = targinfo;
258 const struct ipt_entry *e = entry;
260 if (info->mss == XT_TCPMSS_CLAMP_PMTU &&
261 (hook_mask & ~((1 << NF_IP_FORWARD) |
262 (1 << NF_IP_LOCAL_OUT) |
263 (1 << NF_IP_POST_ROUTING))) != 0) {
264 printk("xt_TCPMSS: path-MTU clamping only supported in "
265 "FORWARD, OUTPUT and POSTROUTING hooks\n");
266 return 0;
268 if (IPT_MATCH_ITERATE(e, find_syn_match))
269 return 1;
270 printk("xt_TCPMSS: Only works on TCP SYN packets\n");
271 return 0;
274 #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
275 static int
276 xt_tcpmss_checkentry6(const char *tablename,
277 const void *entry,
278 const struct xt_target *target,
279 void *targinfo,
280 unsigned int hook_mask)
282 const struct xt_tcpmss_info *info = targinfo;
283 const struct ip6t_entry *e = entry;
285 if (info->mss == XT_TCPMSS_CLAMP_PMTU &&
286 (hook_mask & ~((1 << NF_IP6_FORWARD) |
287 (1 << NF_IP6_LOCAL_OUT) |
288 (1 << NF_IP6_POST_ROUTING))) != 0) {
289 printk("xt_TCPMSS: path-MTU clamping only supported in "
290 "FORWARD, OUTPUT and POSTROUTING hooks\n");
291 return 0;
293 if (IP6T_MATCH_ITERATE(e, find_syn_match))
294 return 1;
295 printk("xt_TCPMSS: Only works on TCP SYN packets\n");
296 return 0;
298 #endif
300 static struct xt_target xt_tcpmss_reg[] = {
302 .family = AF_INET,
303 .name = "TCPMSS",
304 .checkentry = xt_tcpmss_checkentry4,
305 .target = xt_tcpmss_target4,
306 .targetsize = sizeof(struct xt_tcpmss_info),
307 .proto = IPPROTO_TCP,
308 .me = THIS_MODULE,
310 #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
312 .family = AF_INET6,
313 .name = "TCPMSS",
314 .checkentry = xt_tcpmss_checkentry6,
315 .target = xt_tcpmss_target6,
316 .targetsize = sizeof(struct xt_tcpmss_info),
317 .proto = IPPROTO_TCP,
318 .me = THIS_MODULE,
320 #endif
323 static int __init xt_tcpmss_init(void)
325 return xt_register_targets(xt_tcpmss_reg, ARRAY_SIZE(xt_tcpmss_reg));
328 static void __exit xt_tcpmss_fini(void)
330 xt_unregister_targets(xt_tcpmss_reg, ARRAY_SIZE(xt_tcpmss_reg));
333 module_init(xt_tcpmss_init);
334 module_exit(xt_tcpmss_fini);