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