xref: /linux/net/netfilter/xt_tcpmss.c (revision d2912cb1)
1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22e4e6a17SHarald Welte /* Kernel module to match TCP MSS values. */
32e4e6a17SHarald Welte 
42e4e6a17SHarald Welte /* Copyright (C) 2000 Marc Boucher <marc@mbsi.ca>
52e4e6a17SHarald Welte  * Portions (C) 2005 by Harald Welte <laforge@netfilter.org>
62e4e6a17SHarald Welte  */
72e4e6a17SHarald Welte 
82e4e6a17SHarald Welte #include <linux/module.h>
92e4e6a17SHarald Welte #include <linux/skbuff.h>
102e4e6a17SHarald Welte #include <net/tcp.h>
112e4e6a17SHarald Welte 
122e4e6a17SHarald Welte #include <linux/netfilter/xt_tcpmss.h>
132e4e6a17SHarald Welte #include <linux/netfilter/x_tables.h>
142e4e6a17SHarald Welte 
152e4e6a17SHarald Welte #include <linux/netfilter_ipv4/ip_tables.h>
162e4e6a17SHarald Welte #include <linux/netfilter_ipv6/ip6_tables.h>
172e4e6a17SHarald Welte 
182e4e6a17SHarald Welte MODULE_LICENSE("GPL");
192e4e6a17SHarald Welte MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
202ae15b64SJan Engelhardt MODULE_DESCRIPTION("Xtables: TCP MSS match");
212e4e6a17SHarald Welte MODULE_ALIAS("ipt_tcpmss");
2273aaf935SJan Engelhardt MODULE_ALIAS("ip6t_tcpmss");
232e4e6a17SHarald Welte 
241d93a9cbSJan Engelhardt static bool
tcpmss_mt(const struct sk_buff * skb,struct xt_action_param * par)2562fc8051SJan Engelhardt tcpmss_mt(const struct sk_buff *skb, struct xt_action_param *par)
262e4e6a17SHarald Welte {
27f7108a20SJan Engelhardt 	const struct xt_tcpmss_match_info *info = par->matchinfo;
283cf93c96SJan Engelhardt 	const struct tcphdr *th;
293cf93c96SJan Engelhardt 	struct tcphdr _tcph;
302e4e6a17SHarald Welte 	/* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
313cf93c96SJan Engelhardt 	const u_int8_t *op;
323cf93c96SJan Engelhardt 	u8 _opt[15 * 4 - sizeof(_tcph)];
332e4e6a17SHarald Welte 	unsigned int i, optlen;
342e4e6a17SHarald Welte 
352e4e6a17SHarald Welte 	/* If we don't have the whole header, drop packet. */
36f7108a20SJan Engelhardt 	th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph);
372e4e6a17SHarald Welte 	if (th == NULL)
382e4e6a17SHarald Welte 		goto dropit;
392e4e6a17SHarald Welte 
402e4e6a17SHarald Welte 	/* Malformed. */
412e4e6a17SHarald Welte 	if (th->doff*4 < sizeof(*th))
422e4e6a17SHarald Welte 		goto dropit;
432e4e6a17SHarald Welte 
442e4e6a17SHarald Welte 	optlen = th->doff*4 - sizeof(*th);
452e4e6a17SHarald Welte 	if (!optlen)
462e4e6a17SHarald Welte 		goto out;
472e4e6a17SHarald Welte 
482e4e6a17SHarald Welte 	/* Truncated options. */
49f7108a20SJan Engelhardt 	op = skb_header_pointer(skb, par->thoff + sizeof(*th), optlen, _opt);
502e4e6a17SHarald Welte 	if (op == NULL)
512e4e6a17SHarald Welte 		goto dropit;
522e4e6a17SHarald Welte 
532e4e6a17SHarald Welte 	for (i = 0; i < optlen; ) {
542e4e6a17SHarald Welte 		if (op[i] == TCPOPT_MSS
552e4e6a17SHarald Welte 		    && (optlen - i) >= TCPOLEN_MSS
562e4e6a17SHarald Welte 		    && op[i+1] == TCPOLEN_MSS) {
572e4e6a17SHarald Welte 			u_int16_t mssval;
582e4e6a17SHarald Welte 
592e4e6a17SHarald Welte 			mssval = (op[i+2] << 8) | op[i+3];
602e4e6a17SHarald Welte 
61ce556b3aSPatrick McHardy 			return (mssval >= info->mss_min &&
62ce556b3aSPatrick McHardy 				mssval <= info->mss_max) ^ info->invert;
632e4e6a17SHarald Welte 		}
64ce556b3aSPatrick McHardy 		if (op[i] < 2)
65ce556b3aSPatrick McHardy 			i++;
66ce556b3aSPatrick McHardy 		else
67ce556b3aSPatrick McHardy 			i += op[i+1] ? : 1;
682e4e6a17SHarald Welte 	}
692e4e6a17SHarald Welte out:
70ce556b3aSPatrick McHardy 	return info->invert;
712e4e6a17SHarald Welte 
722e4e6a17SHarald Welte dropit:
73b4ba2611SJan Engelhardt 	par->hotdrop = true;
741d93a9cbSJan Engelhardt 	return false;
752e4e6a17SHarald Welte }
762e4e6a17SHarald Welte 
77d3c5ee6dSJan Engelhardt static struct xt_match tcpmss_mt_reg[] __read_mostly = {
784470bbc7SPatrick McHardy 	{
792e4e6a17SHarald Welte 		.name		= "tcpmss",
80ee999d8bSJan Engelhardt 		.family		= NFPROTO_IPV4,
81d3c5ee6dSJan Engelhardt 		.match		= tcpmss_mt,
825d04bff0SPatrick McHardy 		.matchsize	= sizeof(struct xt_tcpmss_match_info),
835d04bff0SPatrick McHardy 		.proto		= IPPROTO_TCP,
842e4e6a17SHarald Welte 		.me		= THIS_MODULE,
854470bbc7SPatrick McHardy 	},
864470bbc7SPatrick McHardy 	{
874470bbc7SPatrick McHardy 		.name		= "tcpmss",
88ee999d8bSJan Engelhardt 		.family		= NFPROTO_IPV6,
89d3c5ee6dSJan Engelhardt 		.match		= tcpmss_mt,
904470bbc7SPatrick McHardy 		.matchsize	= sizeof(struct xt_tcpmss_match_info),
914470bbc7SPatrick McHardy 		.proto		= IPPROTO_TCP,
924470bbc7SPatrick McHardy 		.me		= THIS_MODULE,
934470bbc7SPatrick McHardy 	},
942e4e6a17SHarald Welte };
952e4e6a17SHarald Welte 
tcpmss_mt_init(void)96d3c5ee6dSJan Engelhardt static int __init tcpmss_mt_init(void)
972e4e6a17SHarald Welte {
98d3c5ee6dSJan Engelhardt 	return xt_register_matches(tcpmss_mt_reg, ARRAY_SIZE(tcpmss_mt_reg));
992e4e6a17SHarald Welte }
1002e4e6a17SHarald Welte 
tcpmss_mt_exit(void)101d3c5ee6dSJan Engelhardt static void __exit tcpmss_mt_exit(void)
1022e4e6a17SHarald Welte {
103d3c5ee6dSJan Engelhardt 	xt_unregister_matches(tcpmss_mt_reg, ARRAY_SIZE(tcpmss_mt_reg));
1042e4e6a17SHarald Welte }
1052e4e6a17SHarald Welte 
106d3c5ee6dSJan Engelhardt module_init(tcpmss_mt_init);
107d3c5ee6dSJan Engelhardt module_exit(tcpmss_mt_exit);
108