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                goto invalid;
  50
  51        if (!optlen)
  52                return false;
  53
  54        spin_lock_bh(&dccp_buflock);
  55        op = skb_header_pointer(skb, protoff + optoff, optlen, dccp_optbuf);
  56        if (op == NULL) {
  57                /* If we don't have the whole header, drop packet. */
  58                goto partial;
  59        }
  60
  61        for (i = 0; i < optlen; ) {
  62                if (op[i] == option) {
  63                        spin_unlock_bh(&dccp_buflock);
  64                        return true;
  65                }
  66
  67                if (op[i] < 2)
  68                        i++;
  69                else
  70                        i += op[i+1]?:1;
  71        }
  72
  73        spin_unlock_bh(&dccp_buflock);
  74        return false;
  75
  76partial:
  77        spin_unlock_bh(&dccp_buflock);
  78invalid:
  79        *hotdrop = true;
  80        return false;
  81}
  82
  83
  84static inline bool
  85match_types(const struct dccp_hdr *dh, u_int16_t typemask)
  86{
  87        return typemask & (1 << dh->dccph_type);
  88}
  89
  90static inline bool
  91match_option(u_int8_t option, const struct sk_buff *skb, unsigned int protoff,
  92             const struct dccp_hdr *dh, bool *hotdrop)
  93{
  94        return dccp_find_option(option, skb, protoff, dh, hotdrop);
  95}
  96
  97static bool
  98dccp_mt(const struct sk_buff *skb, const struct xt_match_param *par)
  99{
 100        const struct xt_dccp_info *info = par->matchinfo;
 101        const struct dccp_hdr *dh;
 102        struct dccp_hdr _dh;
 103
 104        if (par->fragoff != 0)
 105                return false;
 106
 107        dh = skb_header_pointer(skb, par->thoff, sizeof(_dh), &_dh);
 108        if (dh == NULL) {
 109                *par->hotdrop = true;
 110                return false;
 111        }
 112
 113        return  DCCHECK(ntohs(dh->dccph_sport) >= info->spts[0]
 114                        && ntohs(dh->dccph_sport) <= info->spts[1],
 115                        XT_DCCP_SRC_PORTS, info->flags, info->invflags)
 116                && DCCHECK(ntohs(dh->dccph_dport) >= info->dpts[0]
 117                        && ntohs(dh->dccph_dport) <= info->dpts[1],
 118                        XT_DCCP_DEST_PORTS, info->flags, info->invflags)
 119                && DCCHECK(match_types(dh, info->typemask),
 120                           XT_DCCP_TYPE, info->flags, info->invflags)
 121                && DCCHECK(match_option(info->option, skb, par->thoff, dh,
 122                                        par->hotdrop),
 123                           XT_DCCP_OPTION, info->flags, info->invflags);
 124}
 125
 126static bool dccp_mt_check(const struct xt_mtchk_param *par)
 127{
 128        const struct xt_dccp_info *info = par->matchinfo;
 129
 130        return !(info->flags & ~XT_DCCP_VALID_FLAGS)
 131                && !(info->invflags & ~XT_DCCP_VALID_FLAGS)
 132                && !(info->invflags & ~info->flags);
 133}
 134
 135static struct xt_match dccp_mt_reg[] __read_mostly = {
 136        {
 137                .name           = "dccp",
 138                .family         = NFPROTO_IPV4,
 139                .checkentry     = dccp_mt_check,
 140                .match          = dccp_mt,
 141                .matchsize      = sizeof(struct xt_dccp_info),
 142                .proto          = IPPROTO_DCCP,
 143                .me             = THIS_MODULE,
 144        },
 145        {
 146                .name           = "dccp",
 147                .family         = NFPROTO_IPV6,
 148                .checkentry     = dccp_mt_check,
 149                .match          = dccp_mt,
 150                .matchsize      = sizeof(struct xt_dccp_info),
 151                .proto          = IPPROTO_DCCP,
 152                .me             = THIS_MODULE,
 153        },
 154};
 155
 156static int __init dccp_mt_init(void)
 157{
 158        int ret;
 159
 160        /* doff is 8 bits, so the maximum option size is (4*256).  Don't put
 161         * this in BSS since DaveM is worried about locked TLB's for kernel
 162         * BSS. */
 163        dccp_optbuf = kmalloc(256 * 4, GFP_KERNEL);
 164        if (!dccp_optbuf)
 165                return -ENOMEM;
 166        ret = xt_register_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
 167        if (ret)
 168                goto out_kfree;
 169        return ret;
 170
 171out_kfree:
 172        kfree(dccp_optbuf);
 173        return ret;
 174}
 175
 176static void __exit dccp_mt_exit(void)
 177{
 178        xt_unregister_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
 179        kfree(dccp_optbuf);
 180}
 181
 182module_init(dccp_mt_init);
 183module_exit(dccp_mt_exit);
 184