linux/net/netfilter/xt_dccp.c
<<
>>
Prefs
   1/*
   2 * iptables module for DCCP protocol header matching
   3 *
   4 * (C) 2005 by Harald Welte <laforge@netfilter.org>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/skbuff.h>
  13#include <linux/slab.h>
  14#include <linux/spinlock.h>
  15#include <net/ip.h>
  16#include <linux/dccp.h>
  17
  18#include <linux/netfilter/x_tables.h>
  19#include <linux/netfilter/xt_dccp.h>
  20
  21#include <linux/netfilter_ipv4/ip_tables.h>
  22#include <linux/netfilter_ipv6/ip6_tables.h>
  23
  24MODULE_LICENSE("GPL");
  25MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
  26MODULE_DESCRIPTION("Xtables: DCCP protocol packet match");
  27MODULE_ALIAS("ipt_dccp");
  28MODULE_ALIAS("ip6t_dccp");
  29
  30#define DCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \
  31                                  || (!!((invflag) & (option)) ^ (cond)))
  32
  33static unsigned char *dccp_optbuf;
  34static DEFINE_SPINLOCK(dccp_buflock);
  35
  36static inline bool
  37dccp_find_option(u_int8_t option,
  38                 const struct sk_buff *skb,
  39                 unsigned int protoff,
  40                 const struct dccp_hdr *dh,
  41                 bool *hotdrop)
  42{
  43        /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
  44        const unsigned char *op;
  45        unsigned int optoff = __dccp_hdr_len(dh);
  46        unsigned int optlen = dh->dccph_doff*4 - __dccp_hdr_len(dh);
  47        unsigned int i;
  48
  49        if (dh->dccph_doff * 4 < __dccp_hdr_len(dh))
  50                goto invalid;
  51
  52        if (!optlen)
  53                return false;
  54
  55        spin_lock_bh(&dccp_buflock);
  56        op = skb_header_pointer(skb, protoff + optoff, optlen, dccp_optbuf);
  57        if (op == NULL) {
  58                /* If we don't have the whole header, drop packet. */
  59                goto partial;
  60        }
  61
  62        for (i = 0; i < optlen; ) {
  63                if (op[i] == option) {
  64                        spin_unlock_bh(&dccp_buflock);
  65                        return true;
  66                }
  67
  68                if (op[i] < 2)
  69                        i++;
  70                else
  71                        i += op[i+1]?:1;
  72        }
  73
  74        spin_unlock_bh(&dccp_buflock);
  75        return false;
  76
  77partial:
  78        spin_unlock_bh(&dccp_buflock);
  79invalid:
  80        *hotdrop = true;
  81        return false;
  82}
  83
  84
  85static inline bool
  86match_types(const struct dccp_hdr *dh, u_int16_t typemask)
  87{
  88        return typemask & (1 << dh->dccph_type);
  89}
  90
  91static inline bool
  92match_option(u_int8_t option, const struct sk_buff *skb, unsigned int protoff,
  93             const struct dccp_hdr *dh, bool *hotdrop)
  94{
  95        return dccp_find_option(option, skb, protoff, dh, hotdrop);
  96}
  97
  98static bool
  99dccp_mt(const struct sk_buff *skb, struct xt_action_param *par)
 100{
 101        const struct xt_dccp_info *info = par->matchinfo;
 102        const struct dccp_hdr *dh;
 103        struct dccp_hdr _dh;
 104
 105        if (par->fragoff != 0)
 106                return false;
 107
 108        dh = skb_header_pointer(skb, par->thoff, sizeof(_dh), &_dh);
 109        if (dh == NULL) {
 110                par->hotdrop = true;
 111                return false;
 112        }
 113
 114        return  DCCHECK(ntohs(dh->dccph_sport) >= info->spts[0]
 115                        && ntohs(dh->dccph_sport) <= info->spts[1],
 116                        XT_DCCP_SRC_PORTS, info->flags, info->invflags)
 117                && DCCHECK(ntohs(dh->dccph_dport) >= info->dpts[0]
 118                        && ntohs(dh->dccph_dport) <= info->dpts[1],
 119                        XT_DCCP_DEST_PORTS, info->flags, info->invflags)
 120                && DCCHECK(match_types(dh, info->typemask),
 121                           XT_DCCP_TYPE, info->flags, info->invflags)
 122                && DCCHECK(match_option(info->option, skb, par->thoff, dh,
 123                                        &par->hotdrop),
 124                           XT_DCCP_OPTION, info->flags, info->invflags);
 125}
 126
 127static int dccp_mt_check(const struct xt_mtchk_param *par)
 128{
 129        const struct xt_dccp_info *info = par->matchinfo;
 130
 131        if (info->flags & ~XT_DCCP_VALID_FLAGS)
 132                return -EINVAL;
 133        if (info->invflags & ~XT_DCCP_VALID_FLAGS)
 134                return -EINVAL;
 135        if (info->invflags & ~info->flags)
 136                return -EINVAL;
 137        return 0;
 138}
 139
 140static struct xt_match dccp_mt_reg[] __read_mostly = {
 141        {
 142                .name           = "dccp",
 143                .family         = NFPROTO_IPV4,
 144                .checkentry     = dccp_mt_check,
 145                .match          = dccp_mt,
 146                .matchsize      = sizeof(struct xt_dccp_info),
 147                .proto          = IPPROTO_DCCP,
 148                .me             = THIS_MODULE,
 149        },
 150        {
 151                .name           = "dccp",
 152                .family         = NFPROTO_IPV6,
 153                .checkentry     = dccp_mt_check,
 154                .match          = dccp_mt,
 155                .matchsize      = sizeof(struct xt_dccp_info),
 156                .proto          = IPPROTO_DCCP,
 157                .me             = THIS_MODULE,
 158        },
 159};
 160
 161static int __init dccp_mt_init(void)
 162{
 163        int ret;
 164
 165        /* doff is 8 bits, so the maximum option size is (4*256).  Don't put
 166         * this in BSS since DaveM is worried about locked TLB's for kernel
 167         * BSS. */
 168        dccp_optbuf = kmalloc(256 * 4, GFP_KERNEL);
 169        if (!dccp_optbuf)
 170                return -ENOMEM;
 171        ret = xt_register_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
 172        if (ret)
 173                goto out_kfree;
 174        return ret;
 175
 176out_kfree:
 177        kfree(dccp_optbuf);
 178        return ret;
 179}
 180
 181static void __exit dccp_mt_exit(void)
 182{
 183        xt_unregister_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
 184        kfree(dccp_optbuf);
 185}
 186
 187module_init(dccp_mt_init);
 188module_exit(dccp_mt_exit);
 189