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