7011c71646f0266eb75c856bc49fea7b5030bd52
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / net / netfilter / xt_TCPMSS.c
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 * Copyright (C) 2007 Patrick McHardy <kaber@trash.net>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12 #include <linux/module.h>
13 #include <linux/skbuff.h>
14 #include <linux/ip.h>
15 #include <linux/gfp.h>
16 #include <linux/ipv6.h>
17 #include <linux/tcp.h>
18 #include <net/dst.h>
19 #include <net/flow.h>
20 #include <net/ipv6.h>
21 #include <net/route.h>
22 #include <net/tcp.h>
23
24 #include <linux/netfilter_ipv4/ip_tables.h>
25 #include <linux/netfilter_ipv6/ip6_tables.h>
26 #include <linux/netfilter/x_tables.h>
27 #include <linux/netfilter/xt_tcpudp.h>
28 #include <linux/netfilter/xt_TCPMSS.h>
29
30 MODULE_LICENSE("GPL");
31 MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
32 MODULE_DESCRIPTION("Xtables: TCP Maximum Segment Size (MSS) adjustment");
33 MODULE_ALIAS("ipt_TCPMSS");
34 MODULE_ALIAS("ip6t_TCPMSS");
35
36 static inline unsigned int
37 optlen(const u_int8_t *opt, unsigned int offset)
38 {
39 /* Beware zero-length options: make finite progress */
40 if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0)
41 return 1;
42 else
43 return opt[offset+1];
44 }
45
46 static int
47 tcpmss_mangle_packet(struct sk_buff *skb,
48 const struct xt_action_param *par,
49 unsigned int in_mtu,
50 unsigned int tcphoff,
51 unsigned int minlen)
52 {
53 const struct xt_tcpmss_info *info = par->targinfo;
54 struct tcphdr *tcph;
55 unsigned int tcplen, i;
56 __be16 oldval;
57 u16 newmss;
58 u8 *opt;
59
60 /* This is a fragment, no TCP header is available */
61 if (par->fragoff != 0)
62 return XT_CONTINUE;
63
64 if (!skb_make_writable(skb, skb->len))
65 return -1;
66
67 tcplen = skb->len - tcphoff;
68 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
69
70 /* Header cannot be larger than the packet */
71 if (tcplen < tcph->doff*4)
72 return -1;
73
74 if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
75 if (dst_mtu(skb_dst(skb)) <= minlen) {
76 net_err_ratelimited("unknown or invalid path-MTU (%u)\n",
77 dst_mtu(skb_dst(skb)));
78 return -1;
79 }
80 if (in_mtu <= minlen) {
81 net_err_ratelimited("unknown or invalid path-MTU (%u)\n",
82 in_mtu);
83 return -1;
84 }
85 newmss = min(dst_mtu(skb_dst(skb)), in_mtu) - minlen;
86 } else
87 newmss = info->mss;
88
89 opt = (u_int8_t *)tcph;
90 for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) {
91 if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS &&
92 opt[i+1] == TCPOLEN_MSS) {
93 u_int16_t oldmss;
94
95 oldmss = (opt[i+2] << 8) | opt[i+3];
96
97 /* Never increase MSS, even when setting it, as
98 * doing so results in problems for hosts that rely
99 * on MSS being set correctly.
100 */
101 if (oldmss <= newmss)
102 return 0;
103
104 opt[i+2] = (newmss & 0xff00) >> 8;
105 opt[i+3] = newmss & 0x00ff;
106
107 inet_proto_csum_replace2(&tcph->check, skb,
108 htons(oldmss), htons(newmss),
109 0);
110 return 0;
111 }
112 }
113
114 /* There is data after the header so the option can't be added
115 without moving it, and doing so may make the SYN packet
116 itself too large. Accept the packet unmodified instead. */
117 if (tcplen > tcph->doff*4)
118 return 0;
119
120 /*
121 * MSS Option not found ?! add it..
122 */
123 if (skb_tailroom(skb) < TCPOLEN_MSS) {
124 if (pskb_expand_head(skb, 0,
125 TCPOLEN_MSS - skb_tailroom(skb),
126 GFP_ATOMIC))
127 return -1;
128 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
129 }
130
131 skb_put(skb, TCPOLEN_MSS);
132
133 /*
134 * IPv4: RFC 1122 states "If an MSS option is not received at
135 * connection setup, TCP MUST assume a default send MSS of 536".
136 * IPv6: RFC 2460 states IPv6 has a minimum MTU of 1280 and a minimum
137 * length IPv6 header of 60, ergo the default MSS value is 1220
138 * Since no MSS was provided, we must use the default values
139 */
140 if (par->family == NFPROTO_IPV4)
141 newmss = min(newmss, (u16)536);
142 else
143 newmss = min(newmss, (u16)1220);
144
145 opt = (u_int8_t *)tcph + sizeof(struct tcphdr);
146 memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr));
147
148 inet_proto_csum_replace2(&tcph->check, skb,
149 htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1);
150 opt[0] = TCPOPT_MSS;
151 opt[1] = TCPOLEN_MSS;
152 opt[2] = (newmss & 0xff00) >> 8;
153 opt[3] = newmss & 0x00ff;
154
155 inet_proto_csum_replace4(&tcph->check, skb, 0, *((__be32 *)opt), 0);
156
157 oldval = ((__be16 *)tcph)[6];
158 tcph->doff += TCPOLEN_MSS/4;
159 inet_proto_csum_replace2(&tcph->check, skb,
160 oldval, ((__be16 *)tcph)[6], 0);
161 return TCPOLEN_MSS;
162 }
163
164 static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb,
165 unsigned int family)
166 {
167 struct flowi fl;
168 const struct nf_afinfo *ai;
169 struct rtable *rt = NULL;
170 u_int32_t mtu = ~0U;
171
172 if (family == PF_INET) {
173 struct flowi4 *fl4 = &fl.u.ip4;
174 memset(fl4, 0, sizeof(*fl4));
175 fl4->daddr = ip_hdr(skb)->saddr;
176 } else {
177 struct flowi6 *fl6 = &fl.u.ip6;
178
179 memset(fl6, 0, sizeof(*fl6));
180 fl6->daddr = ipv6_hdr(skb)->saddr;
181 }
182 rcu_read_lock();
183 ai = nf_get_afinfo(family);
184 if (ai != NULL)
185 ai->route(&init_net, (struct dst_entry **)&rt, &fl, false);
186 rcu_read_unlock();
187
188 if (rt != NULL) {
189 mtu = dst_mtu(&rt->dst);
190 dst_release(&rt->dst);
191 }
192 return mtu;
193 }
194
195 static unsigned int
196 tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par)
197 {
198 struct iphdr *iph = ip_hdr(skb);
199 __be16 newlen;
200 int ret;
201
202 ret = tcpmss_mangle_packet(skb, par,
203 tcpmss_reverse_mtu(skb, PF_INET),
204 iph->ihl * 4,
205 sizeof(*iph) + sizeof(struct tcphdr));
206 if (ret < 0)
207 return NF_DROP;
208 if (ret > 0) {
209 iph = ip_hdr(skb);
210 newlen = htons(ntohs(iph->tot_len) + ret);
211 csum_replace2(&iph->check, iph->tot_len, newlen);
212 iph->tot_len = newlen;
213 }
214 return XT_CONTINUE;
215 }
216
217 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
218 static unsigned int
219 tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par)
220 {
221 struct ipv6hdr *ipv6h = ipv6_hdr(skb);
222 u8 nexthdr;
223 __be16 frag_off;
224 int tcphoff;
225 int ret;
226
227 nexthdr = ipv6h->nexthdr;
228 tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr, &frag_off);
229 if (tcphoff < 0)
230 return NF_DROP;
231 ret = tcpmss_mangle_packet(skb, par,
232 tcpmss_reverse_mtu(skb, PF_INET6),
233 tcphoff,
234 sizeof(*ipv6h) + sizeof(struct tcphdr));
235 if (ret < 0)
236 return NF_DROP;
237 if (ret > 0) {
238 ipv6h = ipv6_hdr(skb);
239 ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) + ret);
240 }
241 return XT_CONTINUE;
242 }
243 #endif
244
245 /* Must specify -p tcp --syn */
246 static inline bool find_syn_match(const struct xt_entry_match *m)
247 {
248 const struct xt_tcp *tcpinfo = (const struct xt_tcp *)m->data;
249
250 if (strcmp(m->u.kernel.match->name, "tcp") == 0 &&
251 tcpinfo->flg_cmp & TCPHDR_SYN &&
252 !(tcpinfo->invflags & XT_TCP_INV_FLAGS))
253 return true;
254
255 return false;
256 }
257
258 static int tcpmss_tg4_check(const struct xt_tgchk_param *par)
259 {
260 const struct xt_tcpmss_info *info = par->targinfo;
261 const struct ipt_entry *e = par->entryinfo;
262 const struct xt_entry_match *ematch;
263
264 if (info->mss == XT_TCPMSS_CLAMP_PMTU &&
265 (par->hook_mask & ~((1 << NF_INET_FORWARD) |
266 (1 << NF_INET_LOCAL_OUT) |
267 (1 << NF_INET_POST_ROUTING))) != 0) {
268 pr_info("path-MTU clamping only supported in "
269 "FORWARD, OUTPUT and POSTROUTING hooks\n");
270 return -EINVAL;
271 }
272 xt_ematch_foreach(ematch, e)
273 if (find_syn_match(ematch))
274 return 0;
275 pr_info("Only works on TCP SYN packets\n");
276 return -EINVAL;
277 }
278
279 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
280 static int tcpmss_tg6_check(const struct xt_tgchk_param *par)
281 {
282 const struct xt_tcpmss_info *info = par->targinfo;
283 const struct ip6t_entry *e = par->entryinfo;
284 const struct xt_entry_match *ematch;
285
286 if (info->mss == XT_TCPMSS_CLAMP_PMTU &&
287 (par->hook_mask & ~((1 << NF_INET_FORWARD) |
288 (1 << NF_INET_LOCAL_OUT) |
289 (1 << NF_INET_POST_ROUTING))) != 0) {
290 pr_info("path-MTU clamping only supported in "
291 "FORWARD, OUTPUT and POSTROUTING hooks\n");
292 return -EINVAL;
293 }
294 xt_ematch_foreach(ematch, e)
295 if (find_syn_match(ematch))
296 return 0;
297 pr_info("Only works on TCP SYN packets\n");
298 return -EINVAL;
299 }
300 #endif
301
302 static struct xt_target tcpmss_tg_reg[] __read_mostly = {
303 {
304 .family = NFPROTO_IPV4,
305 .name = "TCPMSS",
306 .checkentry = tcpmss_tg4_check,
307 .target = tcpmss_tg4,
308 .targetsize = sizeof(struct xt_tcpmss_info),
309 .proto = IPPROTO_TCP,
310 .me = THIS_MODULE,
311 },
312 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
313 {
314 .family = NFPROTO_IPV6,
315 .name = "TCPMSS",
316 .checkentry = tcpmss_tg6_check,
317 .target = tcpmss_tg6,
318 .targetsize = sizeof(struct xt_tcpmss_info),
319 .proto = IPPROTO_TCP,
320 .me = THIS_MODULE,
321 },
322 #endif
323 };
324
325 static int __init tcpmss_tg_init(void)
326 {
327 return xt_register_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
328 }
329
330 static void __exit tcpmss_tg_exit(void)
331 {
332 xt_unregister_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
333 }
334
335 module_init(tcpmss_tg_init);
336 module_exit(tcpmss_tg_exit);