1cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy/*
2cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy * This is a module which is used for setting the MSS option in TCP packets.
3cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy *
4cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy * Copyright (C) 2000 Marc Boucher <marc@mbsi.ca>
5f229f6ce481ceb33a966311722b8ef0cb6c25de7Patrick McHardy * Copyright (C) 2007 Patrick McHardy <kaber@trash.net>
6cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy *
7cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy * This program is free software; you can redistribute it and/or modify
8cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy * it under the terms of the GNU General Public License version 2 as
9cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy * published by the Free Software Foundation.
10cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy */
118bee4bad03c5b601bd6cea123c31025680587cccJan Engelhardt#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy#include <linux/module.h>
13cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy#include <linux/skbuff.h>
14cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy#include <linux/ip.h>
155a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/gfp.h>
16cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy#include <linux/ipv6.h>
17cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy#include <linux/tcp.h>
1837c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt#include <net/dst.h>
1937c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt#include <net/flow.h>
20cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy#include <net/ipv6.h>
2137c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt#include <net/route.h>
22cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy#include <net/tcp.h>
23cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
24cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy#include <linux/netfilter_ipv4/ip_tables.h>
25cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy#include <linux/netfilter_ipv6/ip6_tables.h>
26cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy#include <linux/netfilter/x_tables.h>
27cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy#include <linux/netfilter/xt_tcpudp.h>
28cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy#include <linux/netfilter/xt_TCPMSS.h>
29cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
30cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardyMODULE_LICENSE("GPL");
31cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardyMODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
322ae15b64e6a1608c840c60df38e8e5eef7b2b8c3Jan EngelhardtMODULE_DESCRIPTION("Xtables: TCP Maximum Segment Size (MSS) adjustment");
33cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardyMODULE_ALIAS("ipt_TCPMSS");
34cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardyMODULE_ALIAS("ip6t_TCPMSS");
35cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
36cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardystatic inline unsigned int
37cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardyoptlen(const u_int8_t *opt, unsigned int offset)
38cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy{
39cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	/* Beware zero-length options: make finite progress */
40cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0)
41cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		return 1;
42cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	else
43cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		return opt[offset+1];
44cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy}
45cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
46cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardystatic int
473db05fea51cdb162cfa8f69e9cfb9e228919d2a9Herbert Xutcpmss_mangle_packet(struct sk_buff *skb,
4870d19f805f8c047fc0a28dec9306b3773971c8d9Phil Oester		     const struct xt_action_param *par,
4937c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt		     unsigned int in_mtu,
50cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		     unsigned int tcphoff,
51cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		     unsigned int minlen)
52cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy{
5370d19f805f8c047fc0a28dec9306b3773971c8d9Phil Oester	const struct xt_tcpmss_info *info = par->targinfo;
54cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	struct tcphdr *tcph;
55cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	unsigned int tcplen, i;
56cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	__be16 oldval;
57cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	u16 newmss;
58cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	u8 *opt;
59cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
60b396966c4688522863572927cb30aa874b3ec504Phil Oester	/* This is a fragment, no TCP header is available */
61b396966c4688522863572927cb30aa874b3ec504Phil Oester	if (par->fragoff != 0)
62b396966c4688522863572927cb30aa874b3ec504Phil Oester		return XT_CONTINUE;
63b396966c4688522863572927cb30aa874b3ec504Phil Oester
643db05fea51cdb162cfa8f69e9cfb9e228919d2a9Herbert Xu	if (!skb_make_writable(skb, skb->len))
65cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		return -1;
66cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
673db05fea51cdb162cfa8f69e9cfb9e228919d2a9Herbert Xu	tcplen = skb->len - tcphoff;
683db05fea51cdb162cfa8f69e9cfb9e228919d2a9Herbert Xu	tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
69cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
7010a199394b8f9b4c4e0be6e14a61109a7d891b1bSimon Arlott	/* Header cannot be larger than the packet */
7110a199394b8f9b4c4e0be6e14a61109a7d891b1bSimon Arlott	if (tcplen < tcph->doff*4)
72cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		return -1;
73cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
74cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
75adf30907d63893e4208dfe3f5c88ae12bc2f25d5Eric Dumazet		if (dst_mtu(skb_dst(skb)) <= minlen) {
76e87cc4728f0e2fb663e592a1141742b1d6c63256Joe Perches			net_err_ratelimited("unknown or invalid path-MTU (%u)\n",
77e87cc4728f0e2fb663e592a1141742b1d6c63256Joe Perches					    dst_mtu(skb_dst(skb)));
78cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy			return -1;
79cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		}
8037c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt		if (in_mtu <= minlen) {
81e87cc4728f0e2fb663e592a1141742b1d6c63256Joe Perches			net_err_ratelimited("unknown or invalid path-MTU (%u)\n",
82e87cc4728f0e2fb663e592a1141742b1d6c63256Joe Perches					    in_mtu);
8337c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt			return -1;
8437c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt		}
85adf30907d63893e4208dfe3f5c88ae12bc2f25d5Eric Dumazet		newmss = min(dst_mtu(skb_dst(skb)), in_mtu) - minlen;
86cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	} else
87cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		newmss = info->mss;
88cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
89cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	opt = (u_int8_t *)tcph;
90cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) {
91cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS &&
92cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		    opt[i+1] == TCPOLEN_MSS) {
93cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy			u_int16_t oldmss;
94cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
95cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy			oldmss = (opt[i+2] << 8) | opt[i+3];
96cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
97170080645dac61816455afad807ffeb326ce79e8Benjamin LaHaise			/* Never increase MSS, even when setting it, as
98170080645dac61816455afad807ffeb326ce79e8Benjamin LaHaise			 * doing so results in problems for hosts that rely
99170080645dac61816455afad807ffeb326ce79e8Benjamin LaHaise			 * on MSS being set correctly.
100170080645dac61816455afad807ffeb326ce79e8Benjamin LaHaise			 */
101170080645dac61816455afad807ffeb326ce79e8Benjamin LaHaise			if (oldmss <= newmss)
102cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy				return 0;
103cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
104cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy			opt[i+2] = (newmss & 0xff00) >> 8;
1057c4e36bc172ae1accde835b880fdc4a2c2a3df57Jan Engelhardt			opt[i+3] = newmss & 0x00ff;
106cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
107be0ea7d5da3d99140bde7e5cea328eb111731700Patrick McHardy			inet_proto_csum_replace2(&tcph->check, skb,
108be0ea7d5da3d99140bde7e5cea328eb111731700Patrick McHardy						 htons(oldmss), htons(newmss),
109be0ea7d5da3d99140bde7e5cea328eb111731700Patrick McHardy						 0);
110cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy			return 0;
111cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		}
112cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	}
113cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
11410a199394b8f9b4c4e0be6e14a61109a7d891b1bSimon Arlott	/* There is data after the header so the option can't be added
11510a199394b8f9b4c4e0be6e14a61109a7d891b1bSimon Arlott	   without moving it, and doing so may make the SYN packet
11610a199394b8f9b4c4e0be6e14a61109a7d891b1bSimon Arlott	   itself too large. Accept the packet unmodified instead. */
11710a199394b8f9b4c4e0be6e14a61109a7d891b1bSimon Arlott	if (tcplen > tcph->doff*4)
11810a199394b8f9b4c4e0be6e14a61109a7d891b1bSimon Arlott		return 0;
11910a199394b8f9b4c4e0be6e14a61109a7d891b1bSimon Arlott
120cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	/*
121cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	 * MSS Option not found ?! add it..
122cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	 */
1233db05fea51cdb162cfa8f69e9cfb9e228919d2a9Herbert Xu	if (skb_tailroom(skb) < TCPOLEN_MSS) {
1243db05fea51cdb162cfa8f69e9cfb9e228919d2a9Herbert Xu		if (pskb_expand_head(skb, 0,
1253db05fea51cdb162cfa8f69e9cfb9e228919d2a9Herbert Xu				     TCPOLEN_MSS - skb_tailroom(skb),
1262ca7b0ac022aa0158599178fe1056b1ba9ec8b97Herbert Xu				     GFP_ATOMIC))
127cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy			return -1;
1283db05fea51cdb162cfa8f69e9cfb9e228919d2a9Herbert Xu		tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
129cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	}
130cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
1313db05fea51cdb162cfa8f69e9cfb9e228919d2a9Herbert Xu	skb_put(skb, TCPOLEN_MSS);
132cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
13370d19f805f8c047fc0a28dec9306b3773971c8d9Phil Oester	/*
13470d19f805f8c047fc0a28dec9306b3773971c8d9Phil Oester	 * IPv4: RFC 1122 states "If an MSS option is not received at
13570d19f805f8c047fc0a28dec9306b3773971c8d9Phil Oester	 * connection setup, TCP MUST assume a default send MSS of 536".
13670d19f805f8c047fc0a28dec9306b3773971c8d9Phil Oester	 * IPv6: RFC 2460 states IPv6 has a minimum MTU of 1280 and a minimum
13770d19f805f8c047fc0a28dec9306b3773971c8d9Phil Oester	 * length IPv6 header of 60, ergo the default MSS value is 1220
13870d19f805f8c047fc0a28dec9306b3773971c8d9Phil Oester	 * Since no MSS was provided, we must use the default values
139409b545ac10d9548929557a75ad86540f59a2c83Phil Oester	 */
14070d19f805f8c047fc0a28dec9306b3773971c8d9Phil Oester	if (par->family == NFPROTO_IPV4)
14170d19f805f8c047fc0a28dec9306b3773971c8d9Phil Oester		newmss = min(newmss, (u16)536);
14270d19f805f8c047fc0a28dec9306b3773971c8d9Phil Oester	else
14370d19f805f8c047fc0a28dec9306b3773971c8d9Phil Oester		newmss = min(newmss, (u16)1220);
144409b545ac10d9548929557a75ad86540f59a2c83Phil Oester
145cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	opt = (u_int8_t *)tcph + sizeof(struct tcphdr);
146cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr));
147cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
148be0ea7d5da3d99140bde7e5cea328eb111731700Patrick McHardy	inet_proto_csum_replace2(&tcph->check, skb,
149be0ea7d5da3d99140bde7e5cea328eb111731700Patrick McHardy				 htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1);
150cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	opt[0] = TCPOPT_MSS;
151cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	opt[1] = TCPOLEN_MSS;
152cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	opt[2] = (newmss & 0xff00) >> 8;
1537c4e36bc172ae1accde835b880fdc4a2c2a3df57Jan Engelhardt	opt[3] = newmss & 0x00ff;
154cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
155be0ea7d5da3d99140bde7e5cea328eb111731700Patrick McHardy	inet_proto_csum_replace4(&tcph->check, skb, 0, *((__be32 *)opt), 0);
156cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
157cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	oldval = ((__be16 *)tcph)[6];
158cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	tcph->doff += TCPOLEN_MSS/4;
159be0ea7d5da3d99140bde7e5cea328eb111731700Patrick McHardy	inet_proto_csum_replace2(&tcph->check, skb,
160be0ea7d5da3d99140bde7e5cea328eb111731700Patrick McHardy				 oldval, ((__be16 *)tcph)[6], 0);
161cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	return TCPOLEN_MSS;
162cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy}
163cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
164db1a75bdcc1766dc7e1fae9201ae287dcbcb6c66Jan Engelhardtstatic u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb,
165db1a75bdcc1766dc7e1fae9201ae287dcbcb6c66Jan Engelhardt				    unsigned int family)
16637c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt{
167a1bbb0e698b4ba18c6356564923bb395bed0e576David S. Miller	struct flowi fl;
16837c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt	const struct nf_afinfo *ai;
16937c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt	struct rtable *rt = NULL;
17037c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt	u_int32_t mtu     = ~0U;
17137c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt
172a1bbb0e698b4ba18c6356564923bb395bed0e576David S. Miller	if (family == PF_INET) {
173a1bbb0e698b4ba18c6356564923bb395bed0e576David S. Miller		struct flowi4 *fl4 = &fl.u.ip4;
174a1bbb0e698b4ba18c6356564923bb395bed0e576David S. Miller		memset(fl4, 0, sizeof(*fl4));
175a1bbb0e698b4ba18c6356564923bb395bed0e576David S. Miller		fl4->daddr = ip_hdr(skb)->saddr;
176a1bbb0e698b4ba18c6356564923bb395bed0e576David S. Miller	} else {
177a1bbb0e698b4ba18c6356564923bb395bed0e576David S. Miller		struct flowi6 *fl6 = &fl.u.ip6;
178db1a75bdcc1766dc7e1fae9201ae287dcbcb6c66Jan Engelhardt
179a1bbb0e698b4ba18c6356564923bb395bed0e576David S. Miller		memset(fl6, 0, sizeof(*fl6));
1804e3fd7a06dc20b2d8ec6892233ad2012968fe7b6Alexey Dobriyan		fl6->daddr = ipv6_hdr(skb)->saddr;
181a1bbb0e698b4ba18c6356564923bb395bed0e576David S. Miller	}
18237c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt	rcu_read_lock();
183db1a75bdcc1766dc7e1fae9201ae287dcbcb6c66Jan Engelhardt	ai = nf_get_afinfo(family);
18437c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt	if (ai != NULL)
1850fae2e7740aca7e384c5f337f458897e7e337d58Florian Westphal		ai->route(&init_net, (struct dst_entry **)&rt, &fl, false);
18637c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt	rcu_read_unlock();
18737c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt
18837c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt	if (rt != NULL) {
189d8d1f30b95a635dbd610dcc5eb641aca8f4768cfChangli Gao		mtu = dst_mtu(&rt->dst);
190d8d1f30b95a635dbd610dcc5eb641aca8f4768cfChangli Gao		dst_release(&rt->dst);
19137c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt	}
19237c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt	return mtu;
19337c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt}
19437c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt
195cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardystatic unsigned int
1964b560b447df83368df44bd3712c0c39b1d79ba04Jan Engelhardttcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par)
197cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy{
1983db05fea51cdb162cfa8f69e9cfb9e228919d2a9Herbert Xu	struct iphdr *iph = ip_hdr(skb);
199cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	__be16 newlen;
200cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	int ret;
201cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
20270d19f805f8c047fc0a28dec9306b3773971c8d9Phil Oester	ret = tcpmss_mangle_packet(skb, par,
203db1a75bdcc1766dc7e1fae9201ae287dcbcb6c66Jan Engelhardt				   tcpmss_reverse_mtu(skb, PF_INET),
20437c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt				   iph->ihl * 4,
205cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy				   sizeof(*iph) + sizeof(struct tcphdr));
206cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	if (ret < 0)
207cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		return NF_DROP;
208cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	if (ret > 0) {
2093db05fea51cdb162cfa8f69e9cfb9e228919d2a9Herbert Xu		iph = ip_hdr(skb);
210cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		newlen = htons(ntohs(iph->tot_len) + ret);
211be0ea7d5da3d99140bde7e5cea328eb111731700Patrick McHardy		csum_replace2(&iph->check, iph->tot_len, newlen);
212cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		iph->tot_len = newlen;
213cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	}
214cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	return XT_CONTINUE;
215cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy}
216cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
217c0cd115667bcd23c2a31fe2114beaab3608de68cIgor Maravić#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
218cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardystatic unsigned int
2194b560b447df83368df44bd3712c0c39b1d79ba04Jan Engelhardttcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par)
220cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy{
2213db05fea51cdb162cfa8f69e9cfb9e228919d2a9Herbert Xu	struct ipv6hdr *ipv6h = ipv6_hdr(skb);
222cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	u8 nexthdr;
22375f2811c6460ccc59d83c66059943ce9c9f81a18Jesse Gross	__be16 frag_off;
224cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	int tcphoff;
225cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	int ret;
226cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
227cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	nexthdr = ipv6h->nexthdr;
22875f2811c6460ccc59d83c66059943ce9c9f81a18Jesse Gross	tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr, &frag_off);
2299dc0564e862b1b9a4677dec2c736b12169e03e99Patrick McHardy	if (tcphoff < 0)
230cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		return NF_DROP;
23170d19f805f8c047fc0a28dec9306b3773971c8d9Phil Oester	ret = tcpmss_mangle_packet(skb, par,
232db1a75bdcc1766dc7e1fae9201ae287dcbcb6c66Jan Engelhardt				   tcpmss_reverse_mtu(skb, PF_INET6),
23337c08387fc31a0fe7a570664c93be4f1c1bc0c94Jan Engelhardt				   tcphoff,
234cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy				   sizeof(*ipv6h) + sizeof(struct tcphdr));
235cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	if (ret < 0)
236cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		return NF_DROP;
237cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	if (ret > 0) {
2383db05fea51cdb162cfa8f69e9cfb9e228919d2a9Herbert Xu		ipv6h = ipv6_hdr(skb);
239cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) + ret);
240cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	}
241cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	return XT_CONTINUE;
242cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy}
243cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy#endif
244cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
245cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy/* Must specify -p tcp --syn */
246e1931b784a8de324abf310fa3b5e3f25d3988233Jan Engelhardtstatic inline bool find_syn_match(const struct xt_entry_match *m)
247cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy{
248cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	const struct xt_tcp *tcpinfo = (const struct xt_tcp *)m->data;
249cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
250cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	if (strcmp(m->u.kernel.match->name, "tcp") == 0 &&
251a3433f35a55f7604742cae620c6dc6edfc70db6aChangli Gao	    tcpinfo->flg_cmp & TCPHDR_SYN &&
252cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	    !(tcpinfo->invflags & XT_TCP_INV_FLAGS))
253e1931b784a8de324abf310fa3b5e3f25d3988233Jan Engelhardt		return true;
254cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
255e1931b784a8de324abf310fa3b5e3f25d3988233Jan Engelhardt	return false;
256cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy}
257cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
258135367b8f6a18507af6b9a6910a14b5699415309Jan Engelhardtstatic int tcpmss_tg4_check(const struct xt_tgchk_param *par)
259cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy{
260af5d6dc200eb0fcc6fbd3df1ab4d8969004cb37fJan Engelhardt	const struct xt_tcpmss_info *info = par->targinfo;
261af5d6dc200eb0fcc6fbd3df1ab4d8969004cb37fJan Engelhardt	const struct ipt_entry *e = par->entryinfo;
262dcea992aca82cb08b4674c4c783e325835408d1eJan Engelhardt	const struct xt_entry_match *ematch;
263cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
264cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	if (info->mss == XT_TCPMSS_CLAMP_PMTU &&
265af5d6dc200eb0fcc6fbd3df1ab4d8969004cb37fJan Engelhardt	    (par->hook_mask & ~((1 << NF_INET_FORWARD) |
2666e23ae2a48750bda407a4a58f52a4865d7308bf5Patrick McHardy			   (1 << NF_INET_LOCAL_OUT) |
2676e23ae2a48750bda407a4a58f52a4865d7308bf5Patrick McHardy			   (1 << NF_INET_POST_ROUTING))) != 0) {
2688bee4bad03c5b601bd6cea123c31025680587cccJan Engelhardt		pr_info("path-MTU clamping only supported in "
2698bee4bad03c5b601bd6cea123c31025680587cccJan Engelhardt			"FORWARD, OUTPUT and POSTROUTING hooks\n");
270d6b00a5345ce4e86e8b00a88bb84a2c0c1f69ddcJan Engelhardt		return -EINVAL;
271cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	}
272dcea992aca82cb08b4674c4c783e325835408d1eJan Engelhardt	xt_ematch_foreach(ematch, e)
273dcea992aca82cb08b4674c4c783e325835408d1eJan Engelhardt		if (find_syn_match(ematch))
274d6b00a5345ce4e86e8b00a88bb84a2c0c1f69ddcJan Engelhardt			return 0;
2758bee4bad03c5b601bd6cea123c31025680587cccJan Engelhardt	pr_info("Only works on TCP SYN packets\n");
276d6b00a5345ce4e86e8b00a88bb84a2c0c1f69ddcJan Engelhardt	return -EINVAL;
277cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy}
278cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
279c0cd115667bcd23c2a31fe2114beaab3608de68cIgor Maravić#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
280135367b8f6a18507af6b9a6910a14b5699415309Jan Engelhardtstatic int tcpmss_tg6_check(const struct xt_tgchk_param *par)
281cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy{
282af5d6dc200eb0fcc6fbd3df1ab4d8969004cb37fJan Engelhardt	const struct xt_tcpmss_info *info = par->targinfo;
283af5d6dc200eb0fcc6fbd3df1ab4d8969004cb37fJan Engelhardt	const struct ip6t_entry *e = par->entryinfo;
284dcea992aca82cb08b4674c4c783e325835408d1eJan Engelhardt	const struct xt_entry_match *ematch;
285cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
286cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	if (info->mss == XT_TCPMSS_CLAMP_PMTU &&
287af5d6dc200eb0fcc6fbd3df1ab4d8969004cb37fJan Engelhardt	    (par->hook_mask & ~((1 << NF_INET_FORWARD) |
2886e23ae2a48750bda407a4a58f52a4865d7308bf5Patrick McHardy			   (1 << NF_INET_LOCAL_OUT) |
2896e23ae2a48750bda407a4a58f52a4865d7308bf5Patrick McHardy			   (1 << NF_INET_POST_ROUTING))) != 0) {
2908bee4bad03c5b601bd6cea123c31025680587cccJan Engelhardt		pr_info("path-MTU clamping only supported in "
2918bee4bad03c5b601bd6cea123c31025680587cccJan Engelhardt			"FORWARD, OUTPUT and POSTROUTING hooks\n");
292d6b00a5345ce4e86e8b00a88bb84a2c0c1f69ddcJan Engelhardt		return -EINVAL;
293cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	}
294dcea992aca82cb08b4674c4c783e325835408d1eJan Engelhardt	xt_ematch_foreach(ematch, e)
295dcea992aca82cb08b4674c4c783e325835408d1eJan Engelhardt		if (find_syn_match(ematch))
296d6b00a5345ce4e86e8b00a88bb84a2c0c1f69ddcJan Engelhardt			return 0;
2978bee4bad03c5b601bd6cea123c31025680587cccJan Engelhardt	pr_info("Only works on TCP SYN packets\n");
298d6b00a5345ce4e86e8b00a88bb84a2c0c1f69ddcJan Engelhardt	return -EINVAL;
299cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy}
300cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy#endif
301cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
302d3c5ee6d545b5372fd525ebe16988a5b6efeceb0Jan Engelhardtstatic struct xt_target tcpmss_tg_reg[] __read_mostly = {
303cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	{
304ee999d8b9573df1b547aacdc6d79f86eb79c25cdJan Engelhardt		.family		= NFPROTO_IPV4,
305cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		.name		= "TCPMSS",
306d3c5ee6d545b5372fd525ebe16988a5b6efeceb0Jan Engelhardt		.checkentry	= tcpmss_tg4_check,
307d3c5ee6d545b5372fd525ebe16988a5b6efeceb0Jan Engelhardt		.target		= tcpmss_tg4,
308cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		.targetsize	= sizeof(struct xt_tcpmss_info),
309cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		.proto		= IPPROTO_TCP,
310cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		.me		= THIS_MODULE,
311cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	},
312c0cd115667bcd23c2a31fe2114beaab3608de68cIgor Maravić#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
313cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	{
314ee999d8b9573df1b547aacdc6d79f86eb79c25cdJan Engelhardt		.family		= NFPROTO_IPV6,
315cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		.name		= "TCPMSS",
316d3c5ee6d545b5372fd525ebe16988a5b6efeceb0Jan Engelhardt		.checkentry	= tcpmss_tg6_check,
317d3c5ee6d545b5372fd525ebe16988a5b6efeceb0Jan Engelhardt		.target		= tcpmss_tg6,
318cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		.targetsize	= sizeof(struct xt_tcpmss_info),
319cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		.proto		= IPPROTO_TCP,
320cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy		.me		= THIS_MODULE,
321cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy	},
322cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy#endif
323cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy};
324cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
325d3c5ee6d545b5372fd525ebe16988a5b6efeceb0Jan Engelhardtstatic int __init tcpmss_tg_init(void)
326cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy{
327d3c5ee6d545b5372fd525ebe16988a5b6efeceb0Jan Engelhardt	return xt_register_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
328cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy}
329cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
330d3c5ee6d545b5372fd525ebe16988a5b6efeceb0Jan Engelhardtstatic void __exit tcpmss_tg_exit(void)
331cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy{
332d3c5ee6d545b5372fd525ebe16988a5b6efeceb0Jan Engelhardt	xt_unregister_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
333cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy}
334cdd289a2f833b93e65b9a09a02c37f47a58140a8Patrick McHardy
335d3c5ee6d545b5372fd525ebe16988a5b6efeceb0Jan Engelhardtmodule_init(tcpmss_tg_init);
336d3c5ee6d545b5372fd525ebe16988a5b6efeceb0Jan Engelhardtmodule_exit(tcpmss_tg_exit);
337