0001
0002
0003
0004
0005
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
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
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
0163
0164
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);