]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - net/netfilter/xt_TCPMSS.c
Merge branch 'drm-fixes' of git://people.freedesktop.org/~airlied/linux
[mirror_ubuntu-bionic-kernel.git] / net / netfilter / xt_TCPMSS.c
CommitLineData
cdd289a2
PM
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>
f229f6ce 5 * Copyright (C) 2007 Patrick McHardy <kaber@trash.net>
cdd289a2
PM
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 */
8bee4bad 11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
cdd289a2
PM
12#include <linux/module.h>
13#include <linux/skbuff.h>
14#include <linux/ip.h>
5a0e3ad6 15#include <linux/gfp.h>
cdd289a2
PM
16#include <linux/ipv6.h>
17#include <linux/tcp.h>
37c08387
JE
18#include <net/dst.h>
19#include <net/flow.h>
cdd289a2 20#include <net/ipv6.h>
37c08387 21#include <net/route.h>
cdd289a2
PM
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
30MODULE_LICENSE("GPL");
31MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
2ae15b64 32MODULE_DESCRIPTION("Xtables: TCP Maximum Segment Size (MSS) adjustment");
cdd289a2
PM
33MODULE_ALIAS("ipt_TCPMSS");
34MODULE_ALIAS("ip6t_TCPMSS");
35
36static inline unsigned int
37optlen(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
46static int
3db05fea 47tcpmss_mangle_packet(struct sk_buff *skb,
cdd289a2 48 const struct xt_tcpmss_info *info,
37c08387 49 unsigned int in_mtu,
cdd289a2
PM
50 unsigned int tcphoff,
51 unsigned int minlen)
52{
53 struct tcphdr *tcph;
54 unsigned int tcplen, i;
55 __be16 oldval;
56 u16 newmss;
57 u8 *opt;
58
3db05fea 59 if (!skb_make_writable(skb, skb->len))
cdd289a2
PM
60 return -1;
61
3db05fea
HX
62 tcplen = skb->len - tcphoff;
63 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
cdd289a2 64
10a19939
SA
65 /* Header cannot be larger than the packet */
66 if (tcplen < tcph->doff*4)
cdd289a2 67 return -1;
cdd289a2
PM
68
69 if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
adf30907 70 if (dst_mtu(skb_dst(skb)) <= minlen) {
e87cc472
JP
71 net_err_ratelimited("unknown or invalid path-MTU (%u)\n",
72 dst_mtu(skb_dst(skb)));
cdd289a2
PM
73 return -1;
74 }
37c08387 75 if (in_mtu <= minlen) {
e87cc472
JP
76 net_err_ratelimited("unknown or invalid path-MTU (%u)\n",
77 in_mtu);
37c08387
JE
78 return -1;
79 }
adf30907 80 newmss = min(dst_mtu(skb_dst(skb)), in_mtu) - minlen;
cdd289a2
PM
81 } else
82 newmss = info->mss;
83
84 opt = (u_int8_t *)tcph;
85 for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) {
86 if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS &&
87 opt[i+1] == TCPOLEN_MSS) {
88 u_int16_t oldmss;
89
90 oldmss = (opt[i+2] << 8) | opt[i+3];
91
17008064
BL
92 /* Never increase MSS, even when setting it, as
93 * doing so results in problems for hosts that rely
94 * on MSS being set correctly.
95 */
96 if (oldmss <= newmss)
cdd289a2
PM
97 return 0;
98
99 opt[i+2] = (newmss & 0xff00) >> 8;
7c4e36bc 100 opt[i+3] = newmss & 0x00ff;
cdd289a2 101
be0ea7d5
PM
102 inet_proto_csum_replace2(&tcph->check, skb,
103 htons(oldmss), htons(newmss),
104 0);
cdd289a2
PM
105 return 0;
106 }
107 }
108
10a19939
SA
109 /* There is data after the header so the option can't be added
110 without moving it, and doing so may make the SYN packet
111 itself too large. Accept the packet unmodified instead. */
112 if (tcplen > tcph->doff*4)
113 return 0;
114
cdd289a2
PM
115 /*
116 * MSS Option not found ?! add it..
117 */
3db05fea
HX
118 if (skb_tailroom(skb) < TCPOLEN_MSS) {
119 if (pskb_expand_head(skb, 0,
120 TCPOLEN_MSS - skb_tailroom(skb),
2ca7b0ac 121 GFP_ATOMIC))
cdd289a2 122 return -1;
3db05fea 123 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
cdd289a2
PM
124 }
125
3db05fea 126 skb_put(skb, TCPOLEN_MSS);
cdd289a2
PM
127
128 opt = (u_int8_t *)tcph + sizeof(struct tcphdr);
129 memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr));
130
be0ea7d5
PM
131 inet_proto_csum_replace2(&tcph->check, skb,
132 htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1);
cdd289a2
PM
133 opt[0] = TCPOPT_MSS;
134 opt[1] = TCPOLEN_MSS;
135 opt[2] = (newmss & 0xff00) >> 8;
7c4e36bc 136 opt[3] = newmss & 0x00ff;
cdd289a2 137
be0ea7d5 138 inet_proto_csum_replace4(&tcph->check, skb, 0, *((__be32 *)opt), 0);
cdd289a2
PM
139
140 oldval = ((__be16 *)tcph)[6];
141 tcph->doff += TCPOLEN_MSS/4;
be0ea7d5
PM
142 inet_proto_csum_replace2(&tcph->check, skb,
143 oldval, ((__be16 *)tcph)[6], 0);
cdd289a2
PM
144 return TCPOLEN_MSS;
145}
146
db1a75bd
JE
147static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb,
148 unsigned int family)
37c08387 149{
a1bbb0e6 150 struct flowi fl;
37c08387
JE
151 const struct nf_afinfo *ai;
152 struct rtable *rt = NULL;
153 u_int32_t mtu = ~0U;
154
a1bbb0e6
DM
155 if (family == PF_INET) {
156 struct flowi4 *fl4 = &fl.u.ip4;
157 memset(fl4, 0, sizeof(*fl4));
158 fl4->daddr = ip_hdr(skb)->saddr;
159 } else {
160 struct flowi6 *fl6 = &fl.u.ip6;
db1a75bd 161
a1bbb0e6 162 memset(fl6, 0, sizeof(*fl6));
4e3fd7a0 163 fl6->daddr = ipv6_hdr(skb)->saddr;
a1bbb0e6 164 }
37c08387 165 rcu_read_lock();
db1a75bd 166 ai = nf_get_afinfo(family);
37c08387 167 if (ai != NULL)
0fae2e77 168 ai->route(&init_net, (struct dst_entry **)&rt, &fl, false);
37c08387
JE
169 rcu_read_unlock();
170
171 if (rt != NULL) {
d8d1f30b
CG
172 mtu = dst_mtu(&rt->dst);
173 dst_release(&rt->dst);
37c08387
JE
174 }
175 return mtu;
176}
177
cdd289a2 178static unsigned int
4b560b44 179tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par)
cdd289a2 180{
3db05fea 181 struct iphdr *iph = ip_hdr(skb);
cdd289a2
PM
182 __be16 newlen;
183 int ret;
184
7eb35586 185 ret = tcpmss_mangle_packet(skb, par->targinfo,
db1a75bd 186 tcpmss_reverse_mtu(skb, PF_INET),
37c08387 187 iph->ihl * 4,
cdd289a2
PM
188 sizeof(*iph) + sizeof(struct tcphdr));
189 if (ret < 0)
190 return NF_DROP;
191 if (ret > 0) {
3db05fea 192 iph = ip_hdr(skb);
cdd289a2 193 newlen = htons(ntohs(iph->tot_len) + ret);
be0ea7d5 194 csum_replace2(&iph->check, iph->tot_len, newlen);
cdd289a2
PM
195 iph->tot_len = newlen;
196 }
197 return XT_CONTINUE;
198}
199
c0cd1156 200#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
cdd289a2 201static unsigned int
4b560b44 202tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par)
cdd289a2 203{
3db05fea 204 struct ipv6hdr *ipv6h = ipv6_hdr(skb);
cdd289a2 205 u8 nexthdr;
75f2811c 206 __be16 frag_off;
cdd289a2
PM
207 int tcphoff;
208 int ret;
209
210 nexthdr = ipv6h->nexthdr;
75f2811c 211 tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr, &frag_off);
9dc0564e 212 if (tcphoff < 0)
cdd289a2 213 return NF_DROP;
7eb35586 214 ret = tcpmss_mangle_packet(skb, par->targinfo,
db1a75bd 215 tcpmss_reverse_mtu(skb, PF_INET6),
37c08387 216 tcphoff,
cdd289a2
PM
217 sizeof(*ipv6h) + sizeof(struct tcphdr));
218 if (ret < 0)
219 return NF_DROP;
220 if (ret > 0) {
3db05fea 221 ipv6h = ipv6_hdr(skb);
cdd289a2
PM
222 ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) + ret);
223 }
224 return XT_CONTINUE;
225}
226#endif
227
cdd289a2 228/* Must specify -p tcp --syn */
e1931b78 229static inline bool find_syn_match(const struct xt_entry_match *m)
cdd289a2
PM
230{
231 const struct xt_tcp *tcpinfo = (const struct xt_tcp *)m->data;
232
233 if (strcmp(m->u.kernel.match->name, "tcp") == 0 &&
a3433f35 234 tcpinfo->flg_cmp & TCPHDR_SYN &&
cdd289a2 235 !(tcpinfo->invflags & XT_TCP_INV_FLAGS))
e1931b78 236 return true;
cdd289a2 237
e1931b78 238 return false;
cdd289a2
PM
239}
240
135367b8 241static int tcpmss_tg4_check(const struct xt_tgchk_param *par)
cdd289a2 242{
af5d6dc2
JE
243 const struct xt_tcpmss_info *info = par->targinfo;
244 const struct ipt_entry *e = par->entryinfo;
dcea992a 245 const struct xt_entry_match *ematch;
cdd289a2
PM
246
247 if (info->mss == XT_TCPMSS_CLAMP_PMTU &&
af5d6dc2 248 (par->hook_mask & ~((1 << NF_INET_FORWARD) |
6e23ae2a
PM
249 (1 << NF_INET_LOCAL_OUT) |
250 (1 << NF_INET_POST_ROUTING))) != 0) {
8bee4bad
JE
251 pr_info("path-MTU clamping only supported in "
252 "FORWARD, OUTPUT and POSTROUTING hooks\n");
d6b00a53 253 return -EINVAL;
cdd289a2 254 }
dcea992a
JE
255 xt_ematch_foreach(ematch, e)
256 if (find_syn_match(ematch))
d6b00a53 257 return 0;
8bee4bad 258 pr_info("Only works on TCP SYN packets\n");
d6b00a53 259 return -EINVAL;
cdd289a2
PM
260}
261
c0cd1156 262#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
135367b8 263static int tcpmss_tg6_check(const struct xt_tgchk_param *par)
cdd289a2 264{
af5d6dc2
JE
265 const struct xt_tcpmss_info *info = par->targinfo;
266 const struct ip6t_entry *e = par->entryinfo;
dcea992a 267 const struct xt_entry_match *ematch;
cdd289a2
PM
268
269 if (info->mss == XT_TCPMSS_CLAMP_PMTU &&
af5d6dc2 270 (par->hook_mask & ~((1 << NF_INET_FORWARD) |
6e23ae2a
PM
271 (1 << NF_INET_LOCAL_OUT) |
272 (1 << NF_INET_POST_ROUTING))) != 0) {
8bee4bad
JE
273 pr_info("path-MTU clamping only supported in "
274 "FORWARD, OUTPUT and POSTROUTING hooks\n");
d6b00a53 275 return -EINVAL;
cdd289a2 276 }
dcea992a
JE
277 xt_ematch_foreach(ematch, e)
278 if (find_syn_match(ematch))
d6b00a53 279 return 0;
8bee4bad 280 pr_info("Only works on TCP SYN packets\n");
d6b00a53 281 return -EINVAL;
cdd289a2
PM
282}
283#endif
284
d3c5ee6d 285static struct xt_target tcpmss_tg_reg[] __read_mostly = {
cdd289a2 286 {
ee999d8b 287 .family = NFPROTO_IPV4,
cdd289a2 288 .name = "TCPMSS",
d3c5ee6d
JE
289 .checkentry = tcpmss_tg4_check,
290 .target = tcpmss_tg4,
cdd289a2
PM
291 .targetsize = sizeof(struct xt_tcpmss_info),
292 .proto = IPPROTO_TCP,
293 .me = THIS_MODULE,
294 },
c0cd1156 295#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
cdd289a2 296 {
ee999d8b 297 .family = NFPROTO_IPV6,
cdd289a2 298 .name = "TCPMSS",
d3c5ee6d
JE
299 .checkentry = tcpmss_tg6_check,
300 .target = tcpmss_tg6,
cdd289a2
PM
301 .targetsize = sizeof(struct xt_tcpmss_info),
302 .proto = IPPROTO_TCP,
303 .me = THIS_MODULE,
304 },
305#endif
306};
307
d3c5ee6d 308static int __init tcpmss_tg_init(void)
cdd289a2 309{
d3c5ee6d 310 return xt_register_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
cdd289a2
PM
311}
312
d3c5ee6d 313static void __exit tcpmss_tg_exit(void)
cdd289a2 314{
d3c5ee6d 315 xt_unregister_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
cdd289a2
PM
316}
317
d3c5ee6d
JE
318module_init(tcpmss_tg_init);
319module_exit(tcpmss_tg_exit);