linux/net/netfilter/xt_dccp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * iptables module for DCCP protocol header matching
   4 *
   5 * (C) 2005 by Harald Welte <laforge@netfilter.org>
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/skbuff.h>
  10#include <linux/slab.h>
  11#include <linux/spinlock.h>
  12#include <net/ip.h>
  13#include <linux/dccp.h>
  14
  15#include <linux/netfilter/x_tables.h>
  16#include <linux/netfilter/xt_dccp.h>
  17
  18#include <linux/netfilter_ipv4/ip_tables.h>
  19#include <linux/netfilter_ipv6/ip6_tables.h>
  20
  21MODULE_LICENSE("GPL");
  22MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
  23MODULE_DESCRIPTION("Xtables: DCCP protocol packet match");
  24MODULE_ALIAS("ipt_dccp");
  25MODULE_ALIAS("ip6t_dccp");
  26
  27#define DCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \
  28                                  || (!!((invflag) & (option)) ^ (cond)))
  29
  30static unsigned char *dccp_optbuf;
  31static DEFINE_SPINLOCK(dccp_buflock);
  32
  33static inline bool
  34dccp_find_option(u_int8_t option,
  35                 const struct sk_buff *skb,
  36                 unsigned int protoff,
  37                 const struct dccp_hdr *dh,
  38                 bool *hotdrop)
  39{
  40        /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
  41        const unsigned char *op;
  42        unsigned int optoff = __dccp_hdr_len(dh);
  43        unsigned int optlen = dh->dccph_doff*4 - __dccp_hdr_len(dh);
  44        unsigned int i;
  45
  46        if (dh->dccph_doff * 4 < __dccp_hdr_len(dh))
  47                goto invalid;
  48
  49        if (!optlen)
  50                return false;
  51
  52        spin_lock_bh(&dccp_buflock);
  53        op = skb_header_pointer(skb, protoff + optoff, optlen, dccp_optbuf);
  54        if (op == NULL) {
  55                /* If we don't have the whole header, drop packet. */
  56                goto partial;
  57        }
  58
  59        for (i = 0; i < optlen; ) {
  60                if (op[i] == option) {
  61                        spin_unlock_bh(&dccp_buflock);
  62                        return true;
  63                }
  64
  65                if (op[i] < 2)
  66                        i++;
  67                else
  68                        i += op[i+1]?:1;
  69        }
  70
  71        spin_unlock_bh(&dccp_buflock);
  72        return false;
  73
  74partial:
  75        spin_unlock_bh(&dccp_buflock);
  76invalid:
  77        *hotdrop = true;
  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, struct xt_action_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 int dccp_mt_check(const struct xt_mtchk_param *par)
 125{
 126        const struct xt_dccp_info *info = par->matchinfo;
 127
 128        if (info->flags & ~XT_DCCP_VALID_FLAGS)
 129                return -EINVAL;
 130        if (info->invflags & ~XT_DCCP_VALID_FLAGS)
 131                return -EINVAL;
 132        if (info->invflags & ~info->flags)
 133                return -EINVAL;
 134        return 0;
 135}
 136
 137static struct xt_match dccp_mt_reg[] __read_mostly = {
 138        {
 139                .name           = "dccp",
 140                .family         = NFPROTO_IPV4,
 141                .checkentry     = dccp_mt_check,
 142                .match          = dccp_mt,
 143                .matchsize      = sizeof(struct xt_dccp_info),
 144                .proto          = IPPROTO_DCCP,
 145                .me             = THIS_MODULE,
 146        },
 147        {
 148                .name           = "dccp",
 149                .family         = NFPROTO_IPV6,
 150                .checkentry     = dccp_mt_check,
 151                .match          = dccp_mt,
 152                .matchsize      = sizeof(struct xt_dccp_info),
 153                .proto          = IPPROTO_DCCP,
 154                .me             = THIS_MODULE,
 155        },
 156};
 157
 158static int __init dccp_mt_init(void)
 159{
 160        int ret;
 161
 162        /* doff is 8 bits, so the maximum option size is (4*256).  Don't put
 163         * this in BSS since DaveM is worried about locked TLB's for kernel
 164         * BSS. */
 165        dccp_optbuf = kmalloc(256 * 4, GFP_KERNEL);
 166        if (!dccp_optbuf)
 167                return -ENOMEM;
 168        ret = xt_register_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
 169        if (ret)
 170                goto out_kfree;
 171        return ret;
 172
 173out_kfree:
 174        kfree(dccp_optbuf);
 175        return ret;
 176}
 177
 178static void __exit dccp_mt_exit(void)
 179{
 180        xt_unregister_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
 181        kfree(dccp_optbuf);
 182}
 183
 184module_init(dccp_mt_init);
 185module_exit(dccp_mt_exit);
 186