powerpc/cpumask: Dynamically allocate cpu_sibling_map and cpu_core_map cpumasks
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / net / netfilter / xt_TCPMSS.c
blobc5f4b9919e9a016ef3f3428440e2446571293920
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/gfp.h>
15 #include <linux/ipv6.h>
16 #include <linux/tcp.h>
17 #include <net/dst.h>
18 #include <net/flow.h>
19 #include <net/ipv6.h>
20 #include <net/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("Xtables: 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(skb)) <= minlen) {
70 if (net_ratelimit())
71 printk(KERN_ERR "xt_TCPMSS: "
72 "unknown or invalid path-MTU (%u)\n",
73 dst_mtu(skb_dst(skb)));
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(skb)), 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 inet_proto_csum_replace2(&tcph->check, skb,
105 htons(oldmss), htons(newmss),
107 return 0;
111 /* There is data after the header so the option can't be added
112 without moving it, and doing so may make the SYN packet
113 itself too large. Accept the packet unmodified instead. */
114 if (tcplen > tcph->doff*4)
115 return 0;
118 * MSS Option not found ?! add it..
120 if (skb_tailroom(skb) < TCPOLEN_MSS) {
121 if (pskb_expand_head(skb, 0,
122 TCPOLEN_MSS - skb_tailroom(skb),
123 GFP_ATOMIC))
124 return -1;
125 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
128 skb_put(skb, TCPOLEN_MSS);
130 opt = (u_int8_t *)tcph + sizeof(struct tcphdr);
131 memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr));
133 inet_proto_csum_replace2(&tcph->check, skb,
134 htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1);
135 opt[0] = TCPOPT_MSS;
136 opt[1] = TCPOLEN_MSS;
137 opt[2] = (newmss & 0xff00) >> 8;
138 opt[3] = newmss & 0x00ff;
140 inet_proto_csum_replace4(&tcph->check, skb, 0, *((__be32 *)opt), 0);
142 oldval = ((__be16 *)tcph)[6];
143 tcph->doff += TCPOLEN_MSS/4;
144 inet_proto_csum_replace2(&tcph->check, skb,
145 oldval, ((__be16 *)tcph)[6], 0);
146 return TCPOLEN_MSS;
149 static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb,
150 unsigned int family)
152 struct flowi fl = {};
153 const struct nf_afinfo *ai;
154 struct rtable *rt = NULL;
155 u_int32_t mtu = ~0U;
157 if (family == PF_INET)
158 fl.fl4_dst = ip_hdr(skb)->saddr;
159 else
160 fl.fl6_dst = ipv6_hdr(skb)->saddr;
162 rcu_read_lock();
163 ai = nf_get_afinfo(family);
164 if (ai != NULL)
165 ai->route((struct dst_entry **)&rt, &fl);
166 rcu_read_unlock();
168 if (rt != NULL) {
169 mtu = dst_mtu(&rt->u.dst);
170 dst_release(&rt->u.dst);
172 return mtu;
175 static unsigned int
176 tcpmss_tg4(struct sk_buff *skb, const struct xt_target_param *par)
178 struct iphdr *iph = ip_hdr(skb);
179 __be16 newlen;
180 int ret;
182 ret = tcpmss_mangle_packet(skb, par->targinfo,
183 tcpmss_reverse_mtu(skb, PF_INET),
184 iph->ihl * 4,
185 sizeof(*iph) + sizeof(struct tcphdr));
186 if (ret < 0)
187 return NF_DROP;
188 if (ret > 0) {
189 iph = ip_hdr(skb);
190 newlen = htons(ntohs(iph->tot_len) + ret);
191 csum_replace2(&iph->check, iph->tot_len, newlen);
192 iph->tot_len = newlen;
194 return XT_CONTINUE;
197 #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
198 static unsigned int
199 tcpmss_tg6(struct sk_buff *skb, const struct xt_target_param *par)
201 struct ipv6hdr *ipv6h = ipv6_hdr(skb);
202 u8 nexthdr;
203 int tcphoff;
204 int ret;
206 nexthdr = ipv6h->nexthdr;
207 tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr);
208 if (tcphoff < 0)
209 return NF_DROP;
210 ret = tcpmss_mangle_packet(skb, par->targinfo,
211 tcpmss_reverse_mtu(skb, PF_INET6),
212 tcphoff,
213 sizeof(*ipv6h) + sizeof(struct tcphdr));
214 if (ret < 0)
215 return NF_DROP;
216 if (ret > 0) {
217 ipv6h = ipv6_hdr(skb);
218 ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) + ret);
220 return XT_CONTINUE;
222 #endif
224 #define TH_SYN 0x02
226 /* Must specify -p tcp --syn */
227 static inline bool find_syn_match(const struct xt_entry_match *m)
229 const struct xt_tcp *tcpinfo = (const struct xt_tcp *)m->data;
231 if (strcmp(m->u.kernel.match->name, "tcp") == 0 &&
232 tcpinfo->flg_cmp & TH_SYN &&
233 !(tcpinfo->invflags & XT_TCP_INV_FLAGS))
234 return true;
236 return false;
239 static bool tcpmss_tg4_check(const struct xt_tgchk_param *par)
241 const struct xt_tcpmss_info *info = par->targinfo;
242 const struct ipt_entry *e = par->entryinfo;
243 const struct xt_entry_match *ematch;
245 if (info->mss == XT_TCPMSS_CLAMP_PMTU &&
246 (par->hook_mask & ~((1 << NF_INET_FORWARD) |
247 (1 << NF_INET_LOCAL_OUT) |
248 (1 << NF_INET_POST_ROUTING))) != 0) {
249 printk("xt_TCPMSS: path-MTU clamping only supported in "
250 "FORWARD, OUTPUT and POSTROUTING hooks\n");
251 return false;
253 xt_ematch_foreach(ematch, e)
254 if (find_syn_match(ematch))
255 return true;
256 printk("xt_TCPMSS: Only works on TCP SYN packets\n");
257 return false;
260 #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
261 static bool tcpmss_tg6_check(const struct xt_tgchk_param *par)
263 const struct xt_tcpmss_info *info = par->targinfo;
264 const struct ip6t_entry *e = par->entryinfo;
265 const struct xt_entry_match *ematch;
267 if (info->mss == XT_TCPMSS_CLAMP_PMTU &&
268 (par->hook_mask & ~((1 << NF_INET_FORWARD) |
269 (1 << NF_INET_LOCAL_OUT) |
270 (1 << NF_INET_POST_ROUTING))) != 0) {
271 printk("xt_TCPMSS: path-MTU clamping only supported in "
272 "FORWARD, OUTPUT and POSTROUTING hooks\n");
273 return false;
275 xt_ematch_foreach(ematch, e)
276 if (find_syn_match(ematch))
277 return true;
278 printk("xt_TCPMSS: Only works on TCP SYN packets\n");
279 return false;
281 #endif
283 static struct xt_target tcpmss_tg_reg[] __read_mostly = {
285 .family = NFPROTO_IPV4,
286 .name = "TCPMSS",
287 .checkentry = tcpmss_tg4_check,
288 .target = tcpmss_tg4,
289 .targetsize = sizeof(struct xt_tcpmss_info),
290 .proto = IPPROTO_TCP,
291 .me = THIS_MODULE,
293 #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
295 .family = NFPROTO_IPV6,
296 .name = "TCPMSS",
297 .checkentry = tcpmss_tg6_check,
298 .target = tcpmss_tg6,
299 .targetsize = sizeof(struct xt_tcpmss_info),
300 .proto = IPPROTO_TCP,
301 .me = THIS_MODULE,
303 #endif
306 static int __init tcpmss_tg_init(void)
308 return xt_register_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
311 static void __exit tcpmss_tg_exit(void)
313 xt_unregister_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
316 module_init(tcpmss_tg_init);
317 module_exit(tcpmss_tg_exit);