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
30MODULE_LICENSE("GPL");
31MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
32MODULE_DESCRIPTION("Xtables: TCP Maximum Segment Size (MSS) adjustment");
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
47tcpmss_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
164static 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
195static unsigned int
196tcpmss_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)
218static unsigned int
219tcpmss_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 */
246static 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
258static 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)
280static 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
302static 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
325static int __init tcpmss_tg_init(void)
326{
327	return xt_register_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
328}
329
330static void __exit tcpmss_tg_exit(void)
331{
332	xt_unregister_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
333}
334
335module_init(tcpmss_tg_init);
336module_exit(tcpmss_tg_exit);
337