xref: /openbmc/linux/net/netfilter/xt_dccp.c (revision d2912cb1)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22e4e6a17SHarald Welte /*
32e4e6a17SHarald Welte  * iptables module for DCCP protocol header matching
42e4e6a17SHarald Welte  *
52e4e6a17SHarald Welte  * (C) 2005 by Harald Welte <laforge@netfilter.org>
62e4e6a17SHarald Welte  */
72e4e6a17SHarald Welte 
82e4e6a17SHarald Welte #include <linux/module.h>
92e4e6a17SHarald Welte #include <linux/skbuff.h>
105a0e3ad6STejun Heo #include <linux/slab.h>
112e4e6a17SHarald Welte #include <linux/spinlock.h>
122e4e6a17SHarald Welte #include <net/ip.h>
132e4e6a17SHarald Welte #include <linux/dccp.h>
142e4e6a17SHarald Welte 
152e4e6a17SHarald Welte #include <linux/netfilter/x_tables.h>
162e4e6a17SHarald Welte #include <linux/netfilter/xt_dccp.h>
172e4e6a17SHarald Welte 
182e4e6a17SHarald Welte #include <linux/netfilter_ipv4/ip_tables.h>
192e4e6a17SHarald Welte #include <linux/netfilter_ipv6/ip6_tables.h>
202e4e6a17SHarald Welte 
212e4e6a17SHarald Welte MODULE_LICENSE("GPL");
222e4e6a17SHarald Welte MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
232ae15b64SJan Engelhardt MODULE_DESCRIPTION("Xtables: DCCP protocol packet match");
242e4e6a17SHarald Welte MODULE_ALIAS("ipt_dccp");
2573aaf935SJan Engelhardt MODULE_ALIAS("ip6t_dccp");
262e4e6a17SHarald Welte 
272e4e6a17SHarald Welte #define DCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \
282e4e6a17SHarald Welte 				  || (!!((invflag) & (option)) ^ (cond)))
292e4e6a17SHarald Welte 
302e4e6a17SHarald Welte static unsigned char *dccp_optbuf;
312e4e6a17SHarald Welte static DEFINE_SPINLOCK(dccp_buflock);
322e4e6a17SHarald Welte 
331d93a9cbSJan Engelhardt static inline bool
dccp_find_option(u_int8_t option,const struct sk_buff * skb,unsigned int protoff,const struct dccp_hdr * dh,bool * hotdrop)342e4e6a17SHarald Welte dccp_find_option(u_int8_t option,
352e4e6a17SHarald Welte 		 const struct sk_buff *skb,
362e4e6a17SHarald Welte 		 unsigned int protoff,
372e4e6a17SHarald Welte 		 const struct dccp_hdr *dh,
38cff533acSJan Engelhardt 		 bool *hotdrop)
392e4e6a17SHarald Welte {
402e4e6a17SHarald Welte 	/* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
41a47362a2SJan Engelhardt 	const unsigned char *op;
422e4e6a17SHarald Welte 	unsigned int optoff = __dccp_hdr_len(dh);
432e4e6a17SHarald Welte 	unsigned int optlen = dh->dccph_doff*4 - __dccp_hdr_len(dh);
442e4e6a17SHarald Welte 	unsigned int i;
452e4e6a17SHarald Welte 
4679f55f11SIlpo Järvinen 	if (dh->dccph_doff * 4 < __dccp_hdr_len(dh))
4779f55f11SIlpo Järvinen 		goto invalid;
482e4e6a17SHarald Welte 
492e4e6a17SHarald Welte 	if (!optlen)
501d93a9cbSJan Engelhardt 		return false;
512e4e6a17SHarald Welte 
522e4e6a17SHarald Welte 	spin_lock_bh(&dccp_buflock);
532e4e6a17SHarald Welte 	op = skb_header_pointer(skb, protoff + optoff, optlen, dccp_optbuf);
542e4e6a17SHarald Welte 	if (op == NULL) {
552e4e6a17SHarald Welte 		/* If we don't have the whole header, drop packet. */
5679f55f11SIlpo Järvinen 		goto partial;
572e4e6a17SHarald Welte 	}
582e4e6a17SHarald Welte 
592e4e6a17SHarald Welte 	for (i = 0; i < optlen; ) {
602e4e6a17SHarald Welte 		if (op[i] == option) {
612e4e6a17SHarald Welte 			spin_unlock_bh(&dccp_buflock);
621d93a9cbSJan Engelhardt 			return true;
632e4e6a17SHarald Welte 		}
642e4e6a17SHarald Welte 
652e4e6a17SHarald Welte 		if (op[i] < 2)
662e4e6a17SHarald Welte 			i++;
672e4e6a17SHarald Welte 		else
682e4e6a17SHarald Welte 			i += op[i+1]?:1;
692e4e6a17SHarald Welte 	}
702e4e6a17SHarald Welte 
712e4e6a17SHarald Welte 	spin_unlock_bh(&dccp_buflock);
721d93a9cbSJan Engelhardt 	return false;
7379f55f11SIlpo Järvinen 
7479f55f11SIlpo Järvinen partial:
7579f55f11SIlpo Järvinen 	spin_unlock_bh(&dccp_buflock);
7679f55f11SIlpo Järvinen invalid:
7779f55f11SIlpo Järvinen 	*hotdrop = true;
7879f55f11SIlpo Järvinen 	return false;
792e4e6a17SHarald Welte }
802e4e6a17SHarald Welte 
812e4e6a17SHarald Welte 
821d93a9cbSJan Engelhardt static inline bool
match_types(const struct dccp_hdr * dh,u_int16_t typemask)832e4e6a17SHarald Welte match_types(const struct dccp_hdr *dh, u_int16_t typemask)
842e4e6a17SHarald Welte {
857c4e36bcSJan Engelhardt 	return typemask & (1 << dh->dccph_type);
862e4e6a17SHarald Welte }
872e4e6a17SHarald Welte 
881d93a9cbSJan Engelhardt static inline bool
match_option(u_int8_t option,const struct sk_buff * skb,unsigned int protoff,const struct dccp_hdr * dh,bool * hotdrop)892e4e6a17SHarald Welte match_option(u_int8_t option, const struct sk_buff *skb, unsigned int protoff,
90cff533acSJan Engelhardt 	     const struct dccp_hdr *dh, bool *hotdrop)
912e4e6a17SHarald Welte {
922e4e6a17SHarald Welte 	return dccp_find_option(option, skb, protoff, dh, hotdrop);
932e4e6a17SHarald Welte }
942e4e6a17SHarald Welte 
951d93a9cbSJan Engelhardt static bool
dccp_mt(const struct sk_buff * skb,struct xt_action_param * par)9662fc8051SJan Engelhardt dccp_mt(const struct sk_buff *skb, struct xt_action_param *par)
972e4e6a17SHarald Welte {
98f7108a20SJan Engelhardt 	const struct xt_dccp_info *info = par->matchinfo;
993cf93c96SJan Engelhardt 	const struct dccp_hdr *dh;
1003cf93c96SJan Engelhardt 	struct dccp_hdr _dh;
1012e4e6a17SHarald Welte 
102f7108a20SJan Engelhardt 	if (par->fragoff != 0)
1031d93a9cbSJan Engelhardt 		return false;
1042e4e6a17SHarald Welte 
105f7108a20SJan Engelhardt 	dh = skb_header_pointer(skb, par->thoff, sizeof(_dh), &_dh);
1062e4e6a17SHarald Welte 	if (dh == NULL) {
107b4ba2611SJan Engelhardt 		par->hotdrop = true;
1081d93a9cbSJan Engelhardt 		return false;
1092e4e6a17SHarald Welte 	}
1102e4e6a17SHarald Welte 
1117c4e36bcSJan Engelhardt 	return  DCCHECK(ntohs(dh->dccph_sport) >= info->spts[0]
1127c4e36bcSJan Engelhardt 			&& ntohs(dh->dccph_sport) <= info->spts[1],
1132e4e6a17SHarald Welte 			XT_DCCP_SRC_PORTS, info->flags, info->invflags)
1147c4e36bcSJan Engelhardt 		&& DCCHECK(ntohs(dh->dccph_dport) >= info->dpts[0]
1157c4e36bcSJan Engelhardt 			&& ntohs(dh->dccph_dport) <= info->dpts[1],
1162e4e6a17SHarald Welte 			XT_DCCP_DEST_PORTS, info->flags, info->invflags)
1172e4e6a17SHarald Welte 		&& DCCHECK(match_types(dh, info->typemask),
1182e4e6a17SHarald Welte 			   XT_DCCP_TYPE, info->flags, info->invflags)
119f7108a20SJan Engelhardt 		&& DCCHECK(match_option(info->option, skb, par->thoff, dh,
120b4ba2611SJan Engelhardt 					&par->hotdrop),
1212e4e6a17SHarald Welte 			   XT_DCCP_OPTION, info->flags, info->invflags);
1222e4e6a17SHarald Welte }
1232e4e6a17SHarald Welte 
dccp_mt_check(const struct xt_mtchk_param * par)124b0f38452SJan Engelhardt static int dccp_mt_check(const struct xt_mtchk_param *par)
1252e4e6a17SHarald Welte {
1269b4fce7aSJan Engelhardt 	const struct xt_dccp_info *info = par->matchinfo;
1272e4e6a17SHarald Welte 
1289f567317SJan Engelhardt 	if (info->flags & ~XT_DCCP_VALID_FLAGS)
129bd414ee6SJan Engelhardt 		return -EINVAL;
1309f567317SJan Engelhardt 	if (info->invflags & ~XT_DCCP_VALID_FLAGS)
131bd414ee6SJan Engelhardt 		return -EINVAL;
1329f567317SJan Engelhardt 	if (info->invflags & ~info->flags)
133bd414ee6SJan Engelhardt 		return -EINVAL;
134bd414ee6SJan Engelhardt 	return 0;
1352e4e6a17SHarald Welte }
1362e4e6a17SHarald Welte 
137d3c5ee6dSJan Engelhardt static struct xt_match dccp_mt_reg[] __read_mostly = {
1382e4e6a17SHarald Welte 	{
1392e4e6a17SHarald Welte 		.name 		= "dccp",
140ee999d8bSJan Engelhardt 		.family		= NFPROTO_IPV4,
141d3c5ee6dSJan Engelhardt 		.checkentry	= dccp_mt_check,
142d3c5ee6dSJan Engelhardt 		.match		= dccp_mt,
1435d04bff0SPatrick McHardy 		.matchsize	= sizeof(struct xt_dccp_info),
1445d04bff0SPatrick McHardy 		.proto		= IPPROTO_DCCP,
1452e4e6a17SHarald Welte 		.me 		= THIS_MODULE,
1464470bbc7SPatrick McHardy 	},
1474470bbc7SPatrick McHardy 	{
1484470bbc7SPatrick McHardy 		.name 		= "dccp",
149ee999d8bSJan Engelhardt 		.family		= NFPROTO_IPV6,
150d3c5ee6dSJan Engelhardt 		.checkentry	= dccp_mt_check,
151d3c5ee6dSJan Engelhardt 		.match		= dccp_mt,
1524470bbc7SPatrick McHardy 		.matchsize	= sizeof(struct xt_dccp_info),
1534470bbc7SPatrick McHardy 		.proto		= IPPROTO_DCCP,
1544470bbc7SPatrick McHardy 		.me 		= THIS_MODULE,
1554470bbc7SPatrick McHardy 	},
1562e4e6a17SHarald Welte };
1572e4e6a17SHarald Welte 
dccp_mt_init(void)158d3c5ee6dSJan Engelhardt static int __init dccp_mt_init(void)
1592e4e6a17SHarald Welte {
1602e4e6a17SHarald Welte 	int ret;
1612e4e6a17SHarald Welte 
1622e4e6a17SHarald Welte 	/* doff is 8 bits, so the maximum option size is (4*256).  Don't put
1632e4e6a17SHarald Welte 	 * this in BSS since DaveM is worried about locked TLB's for kernel
1642e4e6a17SHarald Welte 	 * BSS. */
1652e4e6a17SHarald Welte 	dccp_optbuf = kmalloc(256 * 4, GFP_KERNEL);
1662e4e6a17SHarald Welte 	if (!dccp_optbuf)
1672e4e6a17SHarald Welte 		return -ENOMEM;
168d3c5ee6dSJan Engelhardt 	ret = xt_register_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
1692e4e6a17SHarald Welte 	if (ret)
1702e4e6a17SHarald Welte 		goto out_kfree;
1712e4e6a17SHarald Welte 	return ret;
1722e4e6a17SHarald Welte 
1732e4e6a17SHarald Welte out_kfree:
1742e4e6a17SHarald Welte 	kfree(dccp_optbuf);
1752e4e6a17SHarald Welte 	return ret;
1762e4e6a17SHarald Welte }
1772e4e6a17SHarald Welte 
dccp_mt_exit(void)178d3c5ee6dSJan Engelhardt static void __exit dccp_mt_exit(void)
1792e4e6a17SHarald Welte {
180d3c5ee6dSJan Engelhardt 	xt_unregister_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
1812e4e6a17SHarald Welte 	kfree(dccp_optbuf);
1822e4e6a17SHarald Welte }
1832e4e6a17SHarald Welte 
184d3c5ee6dSJan Engelhardt module_init(dccp_mt_init);
185d3c5ee6dSJan Engelhardt module_exit(dccp_mt_exit);
186