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