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