xref: /openbmc/linux/net/netfilter/xt_dccp.c (revision ee999d8b)
12e4e6a17SHarald Welte /*
22e4e6a17SHarald Welte  * iptables module for DCCP protocol header matching
32e4e6a17SHarald Welte  *
42e4e6a17SHarald Welte  * (C) 2005 by Harald Welte <laforge@netfilter.org>
52e4e6a17SHarald Welte  *
62e4e6a17SHarald Welte  * This program is free software; you can redistribute it and/or modify
72e4e6a17SHarald Welte  * it under the terms of the GNU General Public License version 2 as
82e4e6a17SHarald Welte  * published by the Free Software Foundation.
92e4e6a17SHarald Welte  */
102e4e6a17SHarald Welte 
112e4e6a17SHarald Welte #include <linux/module.h>
122e4e6a17SHarald Welte #include <linux/skbuff.h>
132e4e6a17SHarald Welte #include <linux/spinlock.h>
142e4e6a17SHarald Welte #include <net/ip.h>
152e4e6a17SHarald Welte #include <linux/dccp.h>
162e4e6a17SHarald Welte 
172e4e6a17SHarald Welte #include <linux/netfilter/x_tables.h>
182e4e6a17SHarald Welte #include <linux/netfilter/xt_dccp.h>
192e4e6a17SHarald Welte 
202e4e6a17SHarald Welte #include <linux/netfilter_ipv4/ip_tables.h>
212e4e6a17SHarald Welte #include <linux/netfilter_ipv6/ip6_tables.h>
222e4e6a17SHarald Welte 
232e4e6a17SHarald Welte MODULE_LICENSE("GPL");
242e4e6a17SHarald Welte MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
252ae15b64SJan Engelhardt MODULE_DESCRIPTION("Xtables: DCCP protocol packet match");
262e4e6a17SHarald Welte MODULE_ALIAS("ipt_dccp");
2773aaf935SJan Engelhardt MODULE_ALIAS("ip6t_dccp");
282e4e6a17SHarald Welte 
292e4e6a17SHarald Welte #define DCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \
302e4e6a17SHarald Welte 				  || (!!((invflag) & (option)) ^ (cond)))
312e4e6a17SHarald Welte 
322e4e6a17SHarald Welte static unsigned char *dccp_optbuf;
332e4e6a17SHarald Welte static DEFINE_SPINLOCK(dccp_buflock);
342e4e6a17SHarald Welte 
351d93a9cbSJan Engelhardt static inline bool
362e4e6a17SHarald Welte dccp_find_option(u_int8_t option,
372e4e6a17SHarald Welte 		 const struct sk_buff *skb,
382e4e6a17SHarald Welte 		 unsigned int protoff,
392e4e6a17SHarald Welte 		 const struct dccp_hdr *dh,
40cff533acSJan Engelhardt 		 bool *hotdrop)
412e4e6a17SHarald Welte {
422e4e6a17SHarald Welte 	/* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
43a47362a2SJan Engelhardt 	const unsigned char *op;
442e4e6a17SHarald Welte 	unsigned int optoff = __dccp_hdr_len(dh);
452e4e6a17SHarald Welte 	unsigned int optlen = dh->dccph_doff*4 - __dccp_hdr_len(dh);
462e4e6a17SHarald Welte 	unsigned int i;
472e4e6a17SHarald Welte 
482e4e6a17SHarald Welte 	if (dh->dccph_doff * 4 < __dccp_hdr_len(dh)) {
49cff533acSJan Engelhardt 		*hotdrop = true;
501d93a9cbSJan Engelhardt 		return false;
512e4e6a17SHarald Welte 	}
522e4e6a17SHarald Welte 
532e4e6a17SHarald Welte 	if (!optlen)
541d93a9cbSJan Engelhardt 		return false;
552e4e6a17SHarald Welte 
562e4e6a17SHarald Welte 	spin_lock_bh(&dccp_buflock);
572e4e6a17SHarald Welte 	op = skb_header_pointer(skb, protoff + optoff, optlen, dccp_optbuf);
582e4e6a17SHarald Welte 	if (op == NULL) {
592e4e6a17SHarald Welte 		/* If we don't have the whole header, drop packet. */
602e4e6a17SHarald Welte 		spin_unlock_bh(&dccp_buflock);
61cff533acSJan Engelhardt 		*hotdrop = true;
621d93a9cbSJan Engelhardt 		return false;
632e4e6a17SHarald Welte 	}
642e4e6a17SHarald Welte 
652e4e6a17SHarald Welte 	for (i = 0; i < optlen; ) {
662e4e6a17SHarald Welte 		if (op[i] == option) {
672e4e6a17SHarald Welte 			spin_unlock_bh(&dccp_buflock);
681d93a9cbSJan Engelhardt 			return true;
692e4e6a17SHarald Welte 		}
702e4e6a17SHarald Welte 
712e4e6a17SHarald Welte 		if (op[i] < 2)
722e4e6a17SHarald Welte 			i++;
732e4e6a17SHarald Welte 		else
742e4e6a17SHarald Welte 			i += op[i+1]?:1;
752e4e6a17SHarald Welte 	}
762e4e6a17SHarald Welte 
772e4e6a17SHarald Welte 	spin_unlock_bh(&dccp_buflock);
781d93a9cbSJan Engelhardt 	return false;
792e4e6a17SHarald Welte }
802e4e6a17SHarald Welte 
812e4e6a17SHarald Welte 
821d93a9cbSJan Engelhardt static inline bool
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
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
96d3c5ee6dSJan Engelhardt dccp_mt(const struct sk_buff *skb, const struct net_device *in,
97d3c5ee6dSJan Engelhardt         const struct net_device *out, const struct xt_match *match,
98d3c5ee6dSJan Engelhardt         const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop)
992e4e6a17SHarald Welte {
1003e72b2feSPatrick McHardy 	const struct xt_dccp_info *info = matchinfo;
1013cf93c96SJan Engelhardt 	const struct dccp_hdr *dh;
1023cf93c96SJan Engelhardt 	struct dccp_hdr _dh;
1032e4e6a17SHarald Welte 
1042e4e6a17SHarald Welte 	if (offset)
1051d93a9cbSJan Engelhardt 		return false;
1062e4e6a17SHarald Welte 
1072e4e6a17SHarald Welte 	dh = skb_header_pointer(skb, protoff, sizeof(_dh), &_dh);
1082e4e6a17SHarald Welte 	if (dh == NULL) {
109cff533acSJan Engelhardt 		*hotdrop = true;
1101d93a9cbSJan Engelhardt 		return false;
1112e4e6a17SHarald Welte 	}
1122e4e6a17SHarald Welte 
1137c4e36bcSJan Engelhardt 	return  DCCHECK(ntohs(dh->dccph_sport) >= info->spts[0]
1147c4e36bcSJan Engelhardt 			&& ntohs(dh->dccph_sport) <= info->spts[1],
1152e4e6a17SHarald Welte 			XT_DCCP_SRC_PORTS, info->flags, info->invflags)
1167c4e36bcSJan Engelhardt 		&& DCCHECK(ntohs(dh->dccph_dport) >= info->dpts[0]
1177c4e36bcSJan Engelhardt 			&& ntohs(dh->dccph_dport) <= info->dpts[1],
1182e4e6a17SHarald Welte 			XT_DCCP_DEST_PORTS, info->flags, info->invflags)
1192e4e6a17SHarald Welte 		&& DCCHECK(match_types(dh, info->typemask),
1202e4e6a17SHarald Welte 			   XT_DCCP_TYPE, info->flags, info->invflags)
1212e4e6a17SHarald Welte 		&& DCCHECK(match_option(info->option, skb, protoff, dh,
1222e4e6a17SHarald Welte 					hotdrop),
1232e4e6a17SHarald Welte 			   XT_DCCP_OPTION, info->flags, info->invflags);
1242e4e6a17SHarald Welte }
1252e4e6a17SHarald Welte 
126ccb79bdcSJan Engelhardt static bool
127d3c5ee6dSJan Engelhardt dccp_mt_check(const char *tablename, const void *inf,
128d3c5ee6dSJan Engelhardt               const struct xt_match *match, void *matchinfo,
1292e4e6a17SHarald Welte               unsigned int hook_mask)
1302e4e6a17SHarald Welte {
1315d04bff0SPatrick McHardy 	const struct xt_dccp_info *info = matchinfo;
1322e4e6a17SHarald Welte 
1335d04bff0SPatrick McHardy 	return !(info->flags & ~XT_DCCP_VALID_FLAGS)
1342e4e6a17SHarald Welte 		&& !(info->invflags & ~XT_DCCP_VALID_FLAGS)
1352e4e6a17SHarald Welte 		&& !(info->invflags & ~info->flags);
1362e4e6a17SHarald Welte }
1372e4e6a17SHarald Welte 
138d3c5ee6dSJan Engelhardt static struct xt_match dccp_mt_reg[] __read_mostly = {
1392e4e6a17SHarald Welte 	{
1402e4e6a17SHarald Welte 		.name 		= "dccp",
141ee999d8bSJan Engelhardt 		.family		= NFPROTO_IPV4,
142d3c5ee6dSJan Engelhardt 		.checkentry	= dccp_mt_check,
143d3c5ee6dSJan Engelhardt 		.match		= dccp_mt,
1445d04bff0SPatrick McHardy 		.matchsize	= sizeof(struct xt_dccp_info),
1455d04bff0SPatrick McHardy 		.proto		= IPPROTO_DCCP,
1462e4e6a17SHarald Welte 		.me 		= THIS_MODULE,
1474470bbc7SPatrick McHardy 	},
1484470bbc7SPatrick McHardy 	{
1494470bbc7SPatrick McHardy 		.name 		= "dccp",
150ee999d8bSJan Engelhardt 		.family		= NFPROTO_IPV6,
151d3c5ee6dSJan Engelhardt 		.checkentry	= dccp_mt_check,
152d3c5ee6dSJan Engelhardt 		.match		= dccp_mt,
1534470bbc7SPatrick McHardy 		.matchsize	= sizeof(struct xt_dccp_info),
1544470bbc7SPatrick McHardy 		.proto		= IPPROTO_DCCP,
1554470bbc7SPatrick McHardy 		.me 		= THIS_MODULE,
1564470bbc7SPatrick McHardy 	},
1572e4e6a17SHarald Welte };
1582e4e6a17SHarald Welte 
159d3c5ee6dSJan Engelhardt static int __init dccp_mt_init(void)
1602e4e6a17SHarald Welte {
1612e4e6a17SHarald Welte 	int ret;
1622e4e6a17SHarald Welte 
1632e4e6a17SHarald Welte 	/* doff is 8 bits, so the maximum option size is (4*256).  Don't put
1642e4e6a17SHarald Welte 	 * this in BSS since DaveM is worried about locked TLB's for kernel
1652e4e6a17SHarald Welte 	 * BSS. */
1662e4e6a17SHarald Welte 	dccp_optbuf = kmalloc(256 * 4, GFP_KERNEL);
1672e4e6a17SHarald Welte 	if (!dccp_optbuf)
1682e4e6a17SHarald Welte 		return -ENOMEM;
169d3c5ee6dSJan Engelhardt 	ret = xt_register_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
1702e4e6a17SHarald Welte 	if (ret)
1712e4e6a17SHarald Welte 		goto out_kfree;
1722e4e6a17SHarald Welte 	return ret;
1732e4e6a17SHarald Welte 
1742e4e6a17SHarald Welte out_kfree:
1752e4e6a17SHarald Welte 	kfree(dccp_optbuf);
1762e4e6a17SHarald Welte 	return ret;
1772e4e6a17SHarald Welte }
1782e4e6a17SHarald Welte 
179d3c5ee6dSJan Engelhardt static void __exit dccp_mt_exit(void)
1802e4e6a17SHarald Welte {
181d3c5ee6dSJan Engelhardt 	xt_unregister_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
1822e4e6a17SHarald Welte 	kfree(dccp_optbuf);
1832e4e6a17SHarald Welte }
1842e4e6a17SHarald Welte 
185d3c5ee6dSJan Engelhardt module_init(dccp_mt_init);
186d3c5ee6dSJan Engelhardt module_exit(dccp_mt_exit);
187