Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * iptables module for DCCP protocol header matching
0004  *
0005  * (C) 2005 by Harald Welte <laforge@netfilter.org>
0006  */
0007 
0008 #include <linux/module.h>
0009 #include <linux/skbuff.h>
0010 #include <linux/slab.h>
0011 #include <linux/spinlock.h>
0012 #include <net/ip.h>
0013 #include <linux/dccp.h>
0014 
0015 #include <linux/netfilter/x_tables.h>
0016 #include <linux/netfilter/xt_dccp.h>
0017 
0018 #include <linux/netfilter_ipv4/ip_tables.h>
0019 #include <linux/netfilter_ipv6/ip6_tables.h>
0020 
0021 MODULE_LICENSE("GPL");
0022 MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
0023 MODULE_DESCRIPTION("Xtables: DCCP protocol packet match");
0024 MODULE_ALIAS("ipt_dccp");
0025 MODULE_ALIAS("ip6t_dccp");
0026 
0027 #define DCCHECK(cond, option, flag, invflag) (!((flag) & (option)) \
0028                   || (!!((invflag) & (option)) ^ (cond)))
0029 
0030 static unsigned char *dccp_optbuf;
0031 static DEFINE_SPINLOCK(dccp_buflock);
0032 
0033 static inline bool
0034 dccp_find_option(u_int8_t option,
0035          const struct sk_buff *skb,
0036          unsigned int protoff,
0037          const struct dccp_hdr *dh,
0038          bool *hotdrop)
0039 {
0040     /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
0041     const unsigned char *op;
0042     unsigned int optoff = __dccp_hdr_len(dh);
0043     unsigned int optlen = dh->dccph_doff*4 - __dccp_hdr_len(dh);
0044     unsigned int i;
0045 
0046     if (dh->dccph_doff * 4 < __dccp_hdr_len(dh))
0047         goto invalid;
0048 
0049     if (!optlen)
0050         return false;
0051 
0052     spin_lock_bh(&dccp_buflock);
0053     op = skb_header_pointer(skb, protoff + optoff, optlen, dccp_optbuf);
0054     if (op == NULL) {
0055         /* If we don't have the whole header, drop packet. */
0056         goto partial;
0057     }
0058 
0059     for (i = 0; i < optlen; ) {
0060         if (op[i] == option) {
0061             spin_unlock_bh(&dccp_buflock);
0062             return true;
0063         }
0064 
0065         if (op[i] < 2)
0066             i++;
0067         else
0068             i += op[i+1]?:1;
0069     }
0070 
0071     spin_unlock_bh(&dccp_buflock);
0072     return false;
0073 
0074 partial:
0075     spin_unlock_bh(&dccp_buflock);
0076 invalid:
0077     *hotdrop = true;
0078     return false;
0079 }
0080 
0081 
0082 static inline bool
0083 match_types(const struct dccp_hdr *dh, u_int16_t typemask)
0084 {
0085     return typemask & (1 << dh->dccph_type);
0086 }
0087 
0088 static inline bool
0089 match_option(u_int8_t option, const struct sk_buff *skb, unsigned int protoff,
0090          const struct dccp_hdr *dh, bool *hotdrop)
0091 {
0092     return dccp_find_option(option, skb, protoff, dh, hotdrop);
0093 }
0094 
0095 static bool
0096 dccp_mt(const struct sk_buff *skb, struct xt_action_param *par)
0097 {
0098     const struct xt_dccp_info *info = par->matchinfo;
0099     const struct dccp_hdr *dh;
0100     struct dccp_hdr _dh;
0101 
0102     if (par->fragoff != 0)
0103         return false;
0104 
0105     dh = skb_header_pointer(skb, par->thoff, sizeof(_dh), &_dh);
0106     if (dh == NULL) {
0107         par->hotdrop = true;
0108         return false;
0109     }
0110 
0111     return  DCCHECK(ntohs(dh->dccph_sport) >= info->spts[0]
0112             && ntohs(dh->dccph_sport) <= info->spts[1],
0113             XT_DCCP_SRC_PORTS, info->flags, info->invflags)
0114         && DCCHECK(ntohs(dh->dccph_dport) >= info->dpts[0]
0115             && ntohs(dh->dccph_dport) <= info->dpts[1],
0116             XT_DCCP_DEST_PORTS, info->flags, info->invflags)
0117         && DCCHECK(match_types(dh, info->typemask),
0118                XT_DCCP_TYPE, info->flags, info->invflags)
0119         && DCCHECK(match_option(info->option, skb, par->thoff, dh,
0120                     &par->hotdrop),
0121                XT_DCCP_OPTION, info->flags, info->invflags);
0122 }
0123 
0124 static int dccp_mt_check(const struct xt_mtchk_param *par)
0125 {
0126     const struct xt_dccp_info *info = par->matchinfo;
0127 
0128     if (info->flags & ~XT_DCCP_VALID_FLAGS)
0129         return -EINVAL;
0130     if (info->invflags & ~XT_DCCP_VALID_FLAGS)
0131         return -EINVAL;
0132     if (info->invflags & ~info->flags)
0133         return -EINVAL;
0134     return 0;
0135 }
0136 
0137 static struct xt_match dccp_mt_reg[] __read_mostly = {
0138     {
0139         .name       = "dccp",
0140         .family     = NFPROTO_IPV4,
0141         .checkentry = dccp_mt_check,
0142         .match      = dccp_mt,
0143         .matchsize  = sizeof(struct xt_dccp_info),
0144         .proto      = IPPROTO_DCCP,
0145         .me         = THIS_MODULE,
0146     },
0147     {
0148         .name       = "dccp",
0149         .family     = NFPROTO_IPV6,
0150         .checkentry = dccp_mt_check,
0151         .match      = dccp_mt,
0152         .matchsize  = sizeof(struct xt_dccp_info),
0153         .proto      = IPPROTO_DCCP,
0154         .me         = THIS_MODULE,
0155     },
0156 };
0157 
0158 static int __init dccp_mt_init(void)
0159 {
0160     int ret;
0161 
0162     /* doff is 8 bits, so the maximum option size is (4*256).  Don't put
0163      * this in BSS since DaveM is worried about locked TLB's for kernel
0164      * BSS. */
0165     dccp_optbuf = kmalloc(256 * 4, GFP_KERNEL);
0166     if (!dccp_optbuf)
0167         return -ENOMEM;
0168     ret = xt_register_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
0169     if (ret)
0170         goto out_kfree;
0171     return ret;
0172 
0173 out_kfree:
0174     kfree(dccp_optbuf);
0175     return ret;
0176 }
0177 
0178 static void __exit dccp_mt_exit(void)
0179 {
0180     xt_unregister_matches(dccp_mt_reg, ARRAY_SIZE(dccp_mt_reg));
0181     kfree(dccp_optbuf);
0182 }
0183 
0184 module_init(dccp_mt_init);
0185 module_exit(dccp_mt_exit);