xref: /openbmc/linux/net/netfilter/xt_dccp.c (revision 4470bbc7)
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>");
252e4e6a17SHarald Welte MODULE_DESCRIPTION("Match for DCCP protocol packets");
262e4e6a17SHarald Welte MODULE_ALIAS("ipt_dccp");
272e4e6a17SHarald Welte 
282e4e6a17SHarald Welte #define DCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \
292e4e6a17SHarald Welte 		                  || (!!((invflag) & (option)) ^ (cond)))
302e4e6a17SHarald Welte 
312e4e6a17SHarald Welte static unsigned char *dccp_optbuf;
322e4e6a17SHarald Welte static DEFINE_SPINLOCK(dccp_buflock);
332e4e6a17SHarald Welte 
342e4e6a17SHarald Welte static inline int
352e4e6a17SHarald Welte dccp_find_option(u_int8_t option,
362e4e6a17SHarald Welte 		 const struct sk_buff *skb,
372e4e6a17SHarald Welte 		 unsigned int protoff,
382e4e6a17SHarald Welte 		 const struct dccp_hdr *dh,
392e4e6a17SHarald Welte 		 int *hotdrop)
402e4e6a17SHarald Welte {
412e4e6a17SHarald Welte 	/* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
422e4e6a17SHarald Welte 	unsigned char *op;
432e4e6a17SHarald Welte 	unsigned int optoff = __dccp_hdr_len(dh);
442e4e6a17SHarald Welte 	unsigned int optlen = dh->dccph_doff*4 - __dccp_hdr_len(dh);
452e4e6a17SHarald Welte 	unsigned int i;
462e4e6a17SHarald Welte 
472e4e6a17SHarald Welte 	if (dh->dccph_doff * 4 < __dccp_hdr_len(dh)) {
482e4e6a17SHarald Welte 		*hotdrop = 1;
492e4e6a17SHarald Welte 		return 0;
502e4e6a17SHarald Welte 	}
512e4e6a17SHarald Welte 
522e4e6a17SHarald Welte 	if (!optlen)
532e4e6a17SHarald Welte 		return 0;
542e4e6a17SHarald Welte 
552e4e6a17SHarald Welte 	spin_lock_bh(&dccp_buflock);
562e4e6a17SHarald Welte 	op = skb_header_pointer(skb, protoff + optoff, optlen, dccp_optbuf);
572e4e6a17SHarald Welte 	if (op == NULL) {
582e4e6a17SHarald Welte 		/* If we don't have the whole header, drop packet. */
592e4e6a17SHarald Welte 		spin_unlock_bh(&dccp_buflock);
602e4e6a17SHarald Welte 		*hotdrop = 1;
612e4e6a17SHarald Welte 		return 0;
622e4e6a17SHarald Welte 	}
632e4e6a17SHarald Welte 
642e4e6a17SHarald Welte 	for (i = 0; i < optlen; ) {
652e4e6a17SHarald Welte 		if (op[i] == option) {
662e4e6a17SHarald Welte 			spin_unlock_bh(&dccp_buflock);
672e4e6a17SHarald Welte 			return 1;
682e4e6a17SHarald Welte 		}
692e4e6a17SHarald Welte 
702e4e6a17SHarald Welte 		if (op[i] < 2)
712e4e6a17SHarald Welte 			i++;
722e4e6a17SHarald Welte 		else
732e4e6a17SHarald Welte 			i += op[i+1]?:1;
742e4e6a17SHarald Welte 	}
752e4e6a17SHarald Welte 
762e4e6a17SHarald Welte 	spin_unlock_bh(&dccp_buflock);
772e4e6a17SHarald Welte 	return 0;
782e4e6a17SHarald Welte }
792e4e6a17SHarald Welte 
802e4e6a17SHarald Welte 
812e4e6a17SHarald Welte static inline int
822e4e6a17SHarald Welte match_types(const struct dccp_hdr *dh, u_int16_t typemask)
832e4e6a17SHarald Welte {
842e4e6a17SHarald Welte 	return (typemask & (1 << dh->dccph_type));
852e4e6a17SHarald Welte }
862e4e6a17SHarald Welte 
872e4e6a17SHarald Welte static inline int
882e4e6a17SHarald Welte match_option(u_int8_t option, const struct sk_buff *skb, unsigned int protoff,
892e4e6a17SHarald Welte 	     const struct dccp_hdr *dh, int *hotdrop)
902e4e6a17SHarald Welte {
912e4e6a17SHarald Welte 	return dccp_find_option(option, skb, protoff, dh, hotdrop);
922e4e6a17SHarald Welte }
932e4e6a17SHarald Welte 
942e4e6a17SHarald Welte static int
952e4e6a17SHarald Welte match(const struct sk_buff *skb,
962e4e6a17SHarald Welte       const struct net_device *in,
972e4e6a17SHarald Welte       const struct net_device *out,
98c4986734SPatrick McHardy       const struct xt_match *match,
992e4e6a17SHarald Welte       const void *matchinfo,
1002e4e6a17SHarald Welte       int offset,
1012e4e6a17SHarald Welte       unsigned int protoff,
1022e4e6a17SHarald Welte       int *hotdrop)
1032e4e6a17SHarald Welte {
1043e72b2feSPatrick McHardy 	const struct xt_dccp_info *info = matchinfo;
1052e4e6a17SHarald Welte 	struct dccp_hdr _dh, *dh;
1062e4e6a17SHarald Welte 
1072e4e6a17SHarald Welte 	if (offset)
1082e4e6a17SHarald Welte 		return 0;
1092e4e6a17SHarald Welte 
1102e4e6a17SHarald Welte 	dh = skb_header_pointer(skb, protoff, sizeof(_dh), &_dh);
1112e4e6a17SHarald Welte 	if (dh == NULL) {
1122e4e6a17SHarald Welte 		*hotdrop = 1;
1132e4e6a17SHarald Welte 		return 0;
1142e4e6a17SHarald Welte        	}
1152e4e6a17SHarald Welte 
1162e4e6a17SHarald Welte 	return  DCCHECK(((ntohs(dh->dccph_sport) >= info->spts[0])
1172e4e6a17SHarald Welte 			&& (ntohs(dh->dccph_sport) <= info->spts[1])),
1182e4e6a17SHarald Welte 		   	XT_DCCP_SRC_PORTS, info->flags, info->invflags)
1192e4e6a17SHarald Welte 		&& DCCHECK(((ntohs(dh->dccph_dport) >= info->dpts[0])
1202e4e6a17SHarald Welte 			&& (ntohs(dh->dccph_dport) <= info->dpts[1])),
1212e4e6a17SHarald Welte 			XT_DCCP_DEST_PORTS, info->flags, info->invflags)
1222e4e6a17SHarald Welte 		&& DCCHECK(match_types(dh, info->typemask),
1232e4e6a17SHarald Welte 			   XT_DCCP_TYPE, info->flags, info->invflags)
1242e4e6a17SHarald Welte 		&& DCCHECK(match_option(info->option, skb, protoff, dh,
1252e4e6a17SHarald Welte 					hotdrop),
1262e4e6a17SHarald Welte 			   XT_DCCP_OPTION, info->flags, info->invflags);
1272e4e6a17SHarald Welte }
1282e4e6a17SHarald Welte 
1292e4e6a17SHarald Welte static int
1302e4e6a17SHarald Welte checkentry(const char *tablename,
1312e4e6a17SHarald Welte 	   const void *inf,
132c4986734SPatrick McHardy 	   const struct xt_match *match,
1332e4e6a17SHarald Welte 	   void *matchinfo,
1342e4e6a17SHarald Welte 	   unsigned int matchsize,
1352e4e6a17SHarald Welte 	   unsigned int hook_mask)
1362e4e6a17SHarald Welte {
1375d04bff0SPatrick McHardy 	const struct xt_dccp_info *info = matchinfo;
1382e4e6a17SHarald Welte 
1395d04bff0SPatrick McHardy 	return !(info->flags & ~XT_DCCP_VALID_FLAGS)
1402e4e6a17SHarald Welte 		&& !(info->invflags & ~XT_DCCP_VALID_FLAGS)
1412e4e6a17SHarald Welte 		&& !(info->invflags & ~info->flags);
1422e4e6a17SHarald Welte }
1432e4e6a17SHarald Welte 
1444470bbc7SPatrick McHardy static struct xt_match xt_dccp_match[] = {
1452e4e6a17SHarald Welte 	{
1462e4e6a17SHarald Welte 		.name 		= "dccp",
147a45049c5SPablo Neira Ayuso 		.family		= AF_INET,
1484470bbc7SPatrick McHardy 		.checkentry	= checkentry,
1495d04bff0SPatrick McHardy 		.match		= match,
1505d04bff0SPatrick McHardy 		.matchsize	= sizeof(struct xt_dccp_info),
1515d04bff0SPatrick McHardy 		.proto		= IPPROTO_DCCP,
1522e4e6a17SHarald Welte 		.me 		= THIS_MODULE,
1534470bbc7SPatrick McHardy 	},
1544470bbc7SPatrick McHardy 	{
1554470bbc7SPatrick McHardy 		.name 		= "dccp",
1564470bbc7SPatrick McHardy 		.family		= AF_INET6,
1574470bbc7SPatrick McHardy 		.checkentry	= checkentry,
1584470bbc7SPatrick McHardy 		.match		= match,
1594470bbc7SPatrick McHardy 		.matchsize	= sizeof(struct xt_dccp_info),
1604470bbc7SPatrick McHardy 		.proto		= IPPROTO_DCCP,
1614470bbc7SPatrick McHardy 		.me 		= THIS_MODULE,
1624470bbc7SPatrick McHardy 	},
1632e4e6a17SHarald Welte };
1642e4e6a17SHarald Welte 
16565b4b4e8SAndrew Morton static int __init xt_dccp_init(void)
1662e4e6a17SHarald Welte {
1672e4e6a17SHarald Welte 	int ret;
1682e4e6a17SHarald Welte 
1692e4e6a17SHarald Welte 	/* doff is 8 bits, so the maximum option size is (4*256).  Don't put
1702e4e6a17SHarald Welte 	 * this in BSS since DaveM is worried about locked TLB's for kernel
1712e4e6a17SHarald Welte 	 * BSS. */
1722e4e6a17SHarald Welte 	dccp_optbuf = kmalloc(256 * 4, GFP_KERNEL);
1732e4e6a17SHarald Welte 	if (!dccp_optbuf)
1742e4e6a17SHarald Welte 		return -ENOMEM;
1754470bbc7SPatrick McHardy 	ret = xt_register_matches(xt_dccp_match, ARRAY_SIZE(xt_dccp_match));
1762e4e6a17SHarald Welte 	if (ret)
1772e4e6a17SHarald Welte 		goto out_kfree;
1782e4e6a17SHarald Welte 	return ret;
1792e4e6a17SHarald Welte 
1802e4e6a17SHarald Welte out_kfree:
1812e4e6a17SHarald Welte 	kfree(dccp_optbuf);
1822e4e6a17SHarald Welte 	return ret;
1832e4e6a17SHarald Welte }
1842e4e6a17SHarald Welte 
18565b4b4e8SAndrew Morton static void __exit xt_dccp_fini(void)
1862e4e6a17SHarald Welte {
1874470bbc7SPatrick McHardy 	xt_unregister_matches(xt_dccp_match, ARRAY_SIZE(xt_dccp_match));
1882e4e6a17SHarald Welte 	kfree(dccp_optbuf);
1892e4e6a17SHarald Welte }
1902e4e6a17SHarald Welte 
19165b4b4e8SAndrew Morton module_init(xt_dccp_init);
19265b4b4e8SAndrew Morton module_exit(xt_dccp_fini);
193